@ioka-technologies/asyncapi-rust-client-template 0.0.34 → 0.0.35

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.
Files changed (2) hide show
  1. package/dist/common/index.js +3776 -0
  2. package/package.json +7 -1
@@ -0,0 +1,3776 @@
1
+ import "@asyncapi/generator-react-sdk";
2
+ /******/ // The require scope
3
+ /******/ var __webpack_require__ = {};
4
+ /******/
5
+ /************************************************************************/
6
+ /******/ /* webpack/runtime/define property getters */
7
+ /******/ (() => {
8
+ /******/ // define getter functions for harmony exports
9
+ /******/ __webpack_require__.d = (exports, definition) => {
10
+ /******/ for(var key in definition) {
11
+ /******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
12
+ /******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
13
+ /******/ }
14
+ /******/ }
15
+ /******/ };
16
+ /******/ })();
17
+ /******/
18
+ /******/ /* webpack/runtime/hasOwnProperty shorthand */
19
+ /******/ (() => {
20
+ /******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
21
+ /******/ })();
22
+ /******/
23
+ /************************************************************************/
24
+ var __webpack_exports__ = {};
25
+
26
+ ;// ../common/src/string-utils.js
27
+ /**
28
+ * String conversion utilities for AsyncAPI template generation
29
+ *
30
+ * This module provides consistent string conversion functions used across
31
+ * multiple AsyncAPI templates to ensure naming conventions are standardized.
32
+ */
33
+
34
+ /**
35
+ * Converts a string to a valid Rust identifier
36
+ * Handles special characters, keywords, and ensures valid Rust naming conventions
37
+ *
38
+ * @param {string} str - Input string to convert
39
+ * @returns {string} Valid Rust identifier
40
+ */
41
+ function toRustIdentifier(str) {
42
+ if (!str) return 'unknown';
43
+ let identifier = str.replace(/[^a-zA-Z0-9_]/g, '_').replace(/^[0-9]/, '_$&').replace(/_+/g, '_').replace(/^_+|_+$/g, '');
44
+ if (/^[0-9]/.test(identifier)) {
45
+ identifier = 'item_' + identifier;
46
+ }
47
+ if (!identifier) {
48
+ identifier = 'unknown';
49
+ }
50
+ const rustKeywords = ['as', 'break', 'const', 'continue', 'crate', 'else', 'enum', 'extern', 'false', 'fn', 'for', 'if', 'impl', 'in', 'let', 'loop', 'match', 'mod', 'move', 'mut', 'pub', 'ref', 'return', 'self', 'Self', 'static', 'struct', 'super', 'trait', 'true', 'type', 'unsafe', 'use', 'where', 'while', 'async', 'await', 'dyn'];
51
+ if (rustKeywords.includes(identifier)) {
52
+ identifier = identifier + '_';
53
+ }
54
+ return identifier;
55
+ }
56
+
57
+ /**
58
+ * Converts a string to PascalCase Rust type name
59
+ * Handles camelCase, snake_case, and kebab-case inputs
60
+ *
61
+ * @param {string} str - Input string to convert
62
+ * @returns {string} PascalCase Rust type name
63
+ */
64
+ function toRustTypeName(str) {
65
+ if (!str) return 'Unknown';
66
+
67
+ // Ensure str is a string
68
+ const strValue = String(str);
69
+ const identifier = toRustIdentifier(strValue);
70
+
71
+ // Handle camelCase and PascalCase inputs by splitting on capital letters too
72
+ const parts = identifier.replace(/([a-z])([A-Z])/g, '$1_$2') // Insert underscore before capital letters
73
+ .split(/[_\s-]+/) // Split on underscores, spaces, and hyphens
74
+ .filter(part => part.length > 0);
75
+ return parts.map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
76
+ }
77
+
78
+ /**
79
+ * Converts a string to snake_case Rust field name
80
+ *
81
+ * @param {string} str - Input string to convert
82
+ * @returns {string} snake_case Rust field name
83
+ */
84
+ function toRustFieldName(str) {
85
+ if (!str) return 'unknown';
86
+ const identifier = toRustIdentifier(str);
87
+ return identifier.replace(/([A-Z])/g, '_$1').toLowerCase().replace(/^_/, '').replace(/_+/g, '_');
88
+ }
89
+
90
+ /**
91
+ * Converts a string to Rust enum variant name (PascalCase)
92
+ *
93
+ * @param {string} str - Input string to convert
94
+ * @returns {string} PascalCase enum variant name
95
+ */
96
+ function toRustEnumVariant(str) {
97
+ if (!str) return 'Unknown';
98
+ return str.split(/[-_\s]+/).map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
99
+ }
100
+
101
+ /**
102
+ * Converts a string to Rust enum variant with serde rename for lowercase serialization
103
+ *
104
+ * @param {string} str - Input string to convert
105
+ * @returns {object} Object with rustName (PascalCase) and serializedName (lowercase)
106
+ */
107
+ function toRustEnumVariantWithSerde(str) {
108
+ if (!str) return {
109
+ rustName: 'Unknown',
110
+ serializedName: 'unknown'
111
+ };
112
+ const rustName = str.split(/[-_\s]+/).map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
113
+ const serializedName = str.toLowerCase();
114
+ return {
115
+ rustName,
116
+ serializedName
117
+ };
118
+ }
119
+
120
+ /**
121
+ * Converts a string to kebab-case
122
+ * Useful for package names and file names
123
+ *
124
+ * @param {string} str - Input string to convert
125
+ * @returns {string} kebab-case string
126
+ */
127
+ function toKebabCase(str) {
128
+ if (!str) return '';
129
+ return str.toLowerCase().replace(/[^a-z0-9]/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
130
+ }
131
+
132
+ /**
133
+ * Converts a string to PascalCase
134
+ * Useful for class names and type names
135
+ *
136
+ * @param {string} str - Input string to convert
137
+ * @returns {string} PascalCase string
138
+ */
139
+ function toPascalCase(str) {
140
+ if (!str) return '';
141
+ return str.replace(/[^a-zA-Z0-9]/g, ' ').split(' ').map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join('');
142
+ }
143
+
144
+ /**
145
+ * Converts a string to snake_case
146
+ * Useful for Rust identifiers and file names
147
+ *
148
+ * @param {string} str - Input string to convert
149
+ * @returns {string} snake_case string
150
+ */
151
+ function toSnakeCase(str) {
152
+ if (!str) return '';
153
+ return str.toLowerCase().replace(/[^a-z0-9]/g, '_').replace(/_+/g, '_').replace(/^_|_$/g, '');
154
+ }
155
+
156
+ /**
157
+ * Converts a string to camelCase
158
+ * Useful for JavaScript/TypeScript identifiers
159
+ *
160
+ * @param {string} str - Input string to convert
161
+ * @returns {string} camelCase string
162
+ */
163
+ function toCamelCase(str) {
164
+ if (!str) return '';
165
+ const pascalCase = toPascalCase(str);
166
+ return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1);
167
+ }
168
+ ;// ../common/src/message-utils.js
169
+ /**
170
+ * Message processing utilities for AsyncAPI template generation
171
+ *
172
+ * This module provides functions for extracting and processing message information
173
+ * from AsyncAPI specifications, handling various AsyncAPI versions and formats.
174
+ */
175
+
176
+
177
+
178
+ /**
179
+ * Gets the message type name from a message object
180
+ *
181
+ * @param {object} message - AsyncAPI message object
182
+ * @returns {string|null} Message type name or null if not found
183
+ */
184
+ function getMessageTypeName(message) {
185
+ if (!message) return null;
186
+ try {
187
+ // Try AsyncAPI 3.x format first - check _meta and _json properties
188
+ if (message._meta && message._meta.id) {
189
+ return message._meta.id;
190
+ }
191
+ if (message._json && message._json['x-parser-message-name']) {
192
+ return message._json['x-parser-message-name'];
193
+ }
194
+ if (message._json && message._json['x-parser-unique-object-id']) {
195
+ return message._json['x-parser-unique-object-id'];
196
+ }
197
+
198
+ // Try different ways to get the message name
199
+ if (message.name && typeof message.name === 'function') {
200
+ return message.name();
201
+ }
202
+ if (message.name && typeof message.name === 'string') {
203
+ return message.name;
204
+ }
205
+ if (message.title && typeof message.title === 'function') {
206
+ return message.title();
207
+ }
208
+ if (message.title && typeof message.title === 'string') {
209
+ return message.title;
210
+ }
211
+
212
+ // Try to extract from $ref
213
+ if (message.$ref) {
214
+ return message.$ref.split('/').pop();
215
+ }
216
+ return null;
217
+ } catch (e) {
218
+ return null;
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Gets the proper Rust type name from a message
224
+ *
225
+ * @param {object} message - AsyncAPI message object
226
+ * @returns {string} Rust type name
227
+ */
228
+ function getMessageRustTypeName(message) {
229
+ const messageName = getMessageTypeName(message);
230
+ return messageName ? toRustTypeName(messageName) : 'UnknownMessage';
231
+ }
232
+
233
+ /**
234
+ * Gets the payload component schema Rust type name from a message
235
+ * This extracts the actual payload type (component schema) rather than the message wrapper
236
+ *
237
+ * @param {object} message - AsyncAPI message object
238
+ * @returns {string} Rust type name for the payload component schema
239
+ */
240
+ function getPayloadRustTypeName(message) {
241
+ if (!message) return 'UnknownPayload';
242
+ try {
243
+ // First priority: For inline message schemas, get the message name itself
244
+ // This handles cases where the message is defined inline in channels
245
+ const messageName = getMessageTypeName(message);
246
+ if (messageName) {
247
+ // Check if this is a component message reference (has payload.$ref)
248
+ let payload = null;
249
+ if (message.payload && typeof message.payload === 'function') {
250
+ payload = message.payload();
251
+ } else if (message.payload) {
252
+ payload = message.payload;
253
+ }
254
+
255
+ // Check the message's _json for payload information
256
+ const messageJson = message._json || message;
257
+ if (!payload && messageJson.payload) {
258
+ payload = messageJson.payload;
259
+ }
260
+
261
+ // If payload has a $ref, this is a component message - extract the schema name
262
+ if (payload && payload.$ref) {
263
+ const refParts = payload.$ref.split('/');
264
+ const schemaName = refParts[refParts.length - 1];
265
+ return toRustTypeName(schemaName);
266
+ }
267
+
268
+ // For inline message schemas, use the message name directly as the payload type
269
+ // This is the correct approach for messages defined inline in channels
270
+ return toRustTypeName(messageName);
271
+ }
272
+
273
+ // Second priority: Try to get the payload schema reference from the message
274
+ let payload = null;
275
+
276
+ // Try different ways to access the payload
277
+ if (message.payload && typeof message.payload === 'function') {
278
+ payload = message.payload();
279
+ } else if (message.payload) {
280
+ payload = message.payload;
281
+ }
282
+ if (payload) {
283
+ // Check for $ref in the payload (direct reference to component schema)
284
+ if (payload.$ref) {
285
+ const refParts = payload.$ref.split('/');
286
+ const schemaName = refParts[refParts.length - 1];
287
+ return toRustTypeName(schemaName);
288
+ }
289
+
290
+ // Check for resolved $ref using x-parser-schema-id
291
+ if (payload['x-parser-schema-id']) {
292
+ return toRustTypeName(payload['x-parser-schema-id']);
293
+ }
294
+
295
+ // Check for x-parser-schema-id in _json
296
+ if (payload._json && payload._json['x-parser-schema-id']) {
297
+ return toRustTypeName(payload._json['x-parser-schema-id']);
298
+ }
299
+
300
+ // Check for title or name in the payload schema
301
+ if (payload.title) {
302
+ const title = typeof payload.title === 'function' ? payload.title() : payload.title;
303
+ if (title) return toRustTypeName(title);
304
+ }
305
+ if (payload.name) {
306
+ const name = typeof payload.name === 'function' ? payload.name() : payload.name;
307
+ if (name) return toRustTypeName(name);
308
+ }
309
+ }
310
+
311
+ // Check the message's _json for payload information
312
+ const messageJson = message._json || message;
313
+ if (messageJson.payload) {
314
+ if (messageJson.payload.$ref) {
315
+ const refParts = messageJson.payload.$ref.split('/');
316
+ const schemaName = refParts[refParts.length - 1];
317
+ return toRustTypeName(schemaName);
318
+ }
319
+ if (messageJson.payload['x-parser-schema-id']) {
320
+ return toRustTypeName(messageJson.payload['x-parser-schema-id']);
321
+ }
322
+ if (messageJson.payload.title) {
323
+ return toRustTypeName(messageJson.payload.title);
324
+ }
325
+ }
326
+
327
+ // Final fallback: try to extract from message title or name directly
328
+ if (message.title && typeof message.title === 'function') {
329
+ const title = message.title();
330
+ if (title && typeof title === 'string') {
331
+ return toRustTypeName(title);
332
+ }
333
+ } else if (message.title && typeof message.title === 'string') {
334
+ return toRustTypeName(message.title);
335
+ }
336
+ if (message.name && typeof message.name === 'function') {
337
+ const name = message.name();
338
+ if (name && typeof name === 'string') {
339
+ return toRustTypeName(name);
340
+ }
341
+ } else if (message.name && typeof message.name === 'string') {
342
+ return toRustTypeName(message.name);
343
+ }
344
+
345
+ // Check message._json for title/name
346
+ if (messageJson.title && typeof messageJson.title === 'string') {
347
+ return toRustTypeName(messageJson.title);
348
+ }
349
+ if (messageJson.name && typeof messageJson.name === 'string') {
350
+ return toRustTypeName(messageJson.name);
351
+ }
352
+ return 'UnknownPayload';
353
+ } catch (e) {
354
+ console.warn('Error extracting payload type name:', e.message);
355
+ return 'UnknownPayload';
356
+ }
357
+ }
358
+
359
+ /**
360
+ * Gets the TypeScript type name from a message for TypeScript templates
361
+ *
362
+ * @param {object} message - AsyncAPI message object
363
+ * @returns {string} TypeScript type name
364
+ */
365
+ function getMessageTypeScriptTypeName(message) {
366
+ const messageName = getMessageTypeName(message);
367
+ if (!messageName) return 'UnknownMessage';
368
+
369
+ // Convert to PascalCase for TypeScript interfaces
370
+ return messageName.split(/[-_\s]+/).map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
371
+ }
372
+
373
+ /**
374
+ * Gets the payload TypeScript type name from a message
375
+ *
376
+ * @param {object} message - AsyncAPI message object
377
+ * @returns {string} TypeScript type name for the payload
378
+ */
379
+ function getPayloadTypeScriptTypeName(message) {
380
+ if (!message) return 'unknown';
381
+ try {
382
+ // Similar logic to getPayloadRustTypeName but for TypeScript naming
383
+ const messageName = getMessageTypeName(message);
384
+ if (messageName) {
385
+ let payload = null;
386
+ if (message.payload && typeof message.payload === 'function') {
387
+ payload = message.payload();
388
+ } else if (message.payload) {
389
+ payload = message.payload;
390
+ }
391
+ const messageJson = message._json || message;
392
+ if (!payload && messageJson.payload) {
393
+ payload = messageJson.payload;
394
+ }
395
+ if (payload && payload.$ref) {
396
+ const refParts = payload.$ref.split('/');
397
+ const schemaName = refParts[refParts.length - 1];
398
+ return schemaName.split(/[-_\s]+/).map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
399
+ }
400
+ return messageName.split(/[-_\s]+/).map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
401
+ }
402
+ return 'unknown';
403
+ } catch (e) {
404
+ console.warn('Error extracting TypeScript payload type name:', e.message);
405
+ return 'unknown';
406
+ }
407
+ }
408
+
409
+ /**
410
+ * Checks if a message has a payload schema defined
411
+ *
412
+ * @param {object} message - AsyncAPI message object
413
+ * @returns {boolean} True if message has a payload schema
414
+ */
415
+ function messageHasPayload(message) {
416
+ if (!message) return false;
417
+ try {
418
+ // Check for payload function
419
+ if (message.payload && typeof message.payload === 'function') {
420
+ const payload = message.payload();
421
+ return payload !== null && payload !== undefined;
422
+ }
423
+
424
+ // Check for payload property
425
+ if (message.payload) {
426
+ return true;
427
+ }
428
+
429
+ // Check _json for payload
430
+ const messageJson = message._json || message;
431
+ return !!messageJson.payload;
432
+ } catch (e) {
433
+ return false;
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Gets the content type of a message
439
+ *
440
+ * @param {object} message - AsyncAPI message object
441
+ * @returns {string} Content type (e.g., 'application/json')
442
+ */
443
+ function getMessageContentType(message) {
444
+ if (!message) return 'application/json';
445
+ try {
446
+ // Check for contentType function
447
+ if (message.contentType && typeof message.contentType === 'function') {
448
+ return message.contentType() || 'application/json';
449
+ }
450
+
451
+ // Check for contentType property
452
+ if (message.contentType) {
453
+ return message.contentType;
454
+ }
455
+
456
+ // Check _json for contentType
457
+ const messageJson = message._json || message;
458
+ if (messageJson.contentType) {
459
+ return messageJson.contentType;
460
+ }
461
+ return 'application/json';
462
+ } catch (e) {
463
+ return 'application/json';
464
+ }
465
+ }
466
+ ;// ../common/src/channel-utils.js
467
+ /**
468
+ * Channel processing utilities for AsyncAPI template generation
469
+ *
470
+ * This module provides functions for handling channels, parameters, and
471
+ * dynamic channel address resolution across different AsyncAPI templates.
472
+ */
473
+
474
+
475
+
476
+ /**
477
+ * Gets the NATS subject from a channel address
478
+ *
479
+ * @param {object} channel - AsyncAPI channel object
480
+ * @returns {string} NATS subject
481
+ */
482
+ function getNatsSubject(channel) {
483
+ try {
484
+ if (channel.address && typeof channel.address === 'function') {
485
+ return channel.address();
486
+ } else if (channel.address) {
487
+ return channel.address;
488
+ } else if (channel.id && typeof channel.id === 'function') {
489
+ return channel.id();
490
+ } else if (channel.id) {
491
+ return channel.id;
492
+ }
493
+ return 'unknown.subject';
494
+ } catch (e) {
495
+ return 'unknown.subject';
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Gets the channel address from a channel object
501
+ * This is a more generic version of getNatsSubject that works for any protocol
502
+ *
503
+ * @param {object} channel - AsyncAPI channel object
504
+ * @returns {string} Channel address
505
+ */
506
+ function getChannelAddress(channel) {
507
+ try {
508
+ if (channel.address && typeof channel.address === 'function') {
509
+ return channel.address();
510
+ } else if (channel.address) {
511
+ return channel.address;
512
+ } else if (channel.id && typeof channel.id === 'function') {
513
+ return channel.id();
514
+ } else if (channel.id) {
515
+ return channel.id;
516
+ }
517
+ return 'unknown.address';
518
+ } catch (e) {
519
+ return 'unknown.address';
520
+ }
521
+ }
522
+
523
+ /**
524
+ * Checks if a channel address contains variables (dynamic channel)
525
+ *
526
+ * @param {string} address - Channel address
527
+ * @returns {boolean} True if the address contains variables
528
+ */
529
+ function isDynamicChannel(address) {
530
+ if (!address || typeof address !== 'string') return false;
531
+ return /\{[^}]+\}/.test(address);
532
+ }
533
+
534
+ /**
535
+ * Extracts variable names from a channel address
536
+ *
537
+ * @param {string} address - Channel address with variables
538
+ * @returns {Array<string>} Array of variable names
539
+ */
540
+ function extractChannelVariables(address) {
541
+ if (!address || typeof address !== 'string') return [];
542
+ const matches = address.match(/\{([^}]+)\}/g);
543
+ if (!matches) return [];
544
+ return matches.map(match => match.slice(1, -1)); // Remove { and }
545
+ }
546
+
547
+ /**
548
+ * Extracts channel parameters from a dynamic channel address
549
+ *
550
+ * @param {string} channelAddress - Channel address like "device.{device_id}" or "user.{user_id}.notifications"
551
+ * @returns {Array} Array of parameter objects with name and rustName
552
+ */
553
+ function extractChannelParameters(channelAddress) {
554
+ if (!channelAddress) return [];
555
+ const parameterRegex = /\{([^}]+)\}/g;
556
+ const parameters = [];
557
+ let match;
558
+ while ((match = parameterRegex.exec(channelAddress)) !== null) {
559
+ const paramName = match[1];
560
+ parameters.push({
561
+ name: paramName,
562
+ rustName: toRustFieldName(paramName),
563
+ placeholder: match[0] // The full {param_name} string
564
+ });
565
+ }
566
+ return parameters;
567
+ }
568
+
569
+ /**
570
+ * Gets channel parameters from a channel object
571
+ *
572
+ * @param {object} channel - AsyncAPI channel object
573
+ * @returns {Array<object>} Array of parameter objects with name and description
574
+ */
575
+ function getChannelParameters(channel) {
576
+ try {
577
+ const parameters = [];
578
+
579
+ // Try to get parameters from the channel
580
+ let channelParams = null;
581
+ if (channel.parameters && typeof channel.parameters === 'function') {
582
+ channelParams = channel.parameters();
583
+ } else if (channel.parameters) {
584
+ channelParams = channel.parameters;
585
+ } else if (channel._json && channel._json.parameters) {
586
+ channelParams = channel._json.parameters;
587
+ }
588
+ if (channelParams) {
589
+ // Handle different parameter formats
590
+ if (typeof channelParams === 'object') {
591
+ for (const [paramName, paramDef] of Object.entries(channelParams)) {
592
+ // Skip internal AsyncAPI parser properties
593
+ if (paramName.startsWith('_') || paramName === 'collections' || paramName === 'meta') {
594
+ continue;
595
+ }
596
+ let description = 'Channel parameter';
597
+ if (paramDef && typeof paramDef === 'object') {
598
+ if (typeof paramDef.description === 'string') {
599
+ description = paramDef.description;
600
+ } else if (typeof paramDef.description === 'function') {
601
+ try {
602
+ description = paramDef.description();
603
+ } catch (e) {
604
+ description = 'Channel parameter';
605
+ }
606
+ } else if (paramDef._json && paramDef._json.description) {
607
+ description = paramDef._json.description;
608
+ }
609
+ } else if (typeof paramDef === 'string') {
610
+ description = paramDef;
611
+ }
612
+ parameters.push({
613
+ name: paramName,
614
+ description: description,
615
+ rustName: toRustFieldName(paramName),
616
+ rustType: 'String' // For now, assume all parameters are strings
617
+ });
618
+ }
619
+ }
620
+ }
621
+ return parameters;
622
+ } catch (e) {
623
+ console.warn('Error extracting channel parameters:', e.message);
624
+ return [];
625
+ }
626
+ }
627
+
628
+ /**
629
+ * Resolves a dynamic channel address with provided variable values
630
+ *
631
+ * @param {string} address - Channel address template with variables
632
+ * @param {object} variables - Object mapping variable names to values
633
+ * @returns {string} Resolved channel address
634
+ */
635
+ function resolveChannelAddress(address, variables) {
636
+ if (!address || typeof address !== 'string') return address;
637
+ if (!variables || typeof variables !== 'object') return address;
638
+ let resolved = address;
639
+ for (const [varName, varValue] of Object.entries(variables)) {
640
+ const placeholder = `{${varName}}`;
641
+ resolved = resolved.replace(new RegExp(placeholder.replace(/[{}]/g, '\\$&'), 'g'), varValue);
642
+ }
643
+ return resolved;
644
+ }
645
+
646
+ /**
647
+ * Checks if a channel has dynamic parameters
648
+ *
649
+ * @param {object} channel - AsyncAPI channel object
650
+ * @returns {boolean} True if the channel has parameters
651
+ */
652
+ function channelHasParameters(channel) {
653
+ const address = getChannelAddress(channel);
654
+ return isDynamicChannel(address);
655
+ }
656
+
657
+ /**
658
+ * Generates Rust function parameters for dynamic channel parameters
659
+ *
660
+ * @param {Array} channelParameters - Array of channel parameter objects
661
+ * @returns {string} Rust function parameter string
662
+ */
663
+ function generateChannelParameterArgs(channelParameters) {
664
+ if (!channelParameters || channelParameters.length === 0) {
665
+ return '';
666
+ }
667
+ return channelParameters.map(param => `${param.rustName}: String`).join(', ') + ', ';
668
+ }
669
+
670
+ /**
671
+ * Generates Rust format string and arguments for dynamic channel resolution
672
+ *
673
+ * @param {string} channelAddress - Original channel address with parameters
674
+ * @param {Array} channelParameters - Array of channel parameter objects
675
+ * @returns {object} Object with formatString and formatArgs
676
+ */
677
+ function generateChannelFormatting(channelAddress, channelParameters) {
678
+ if (!channelParameters || channelParameters.length === 0) {
679
+ return {
680
+ formatString: `"${channelAddress}".to_string()`,
681
+ formatArgs: ''
682
+ };
683
+ }
684
+
685
+ // Replace parameter placeholders with format placeholders
686
+ let formatString = channelAddress;
687
+ const formatArgs = [];
688
+ for (const param of channelParameters) {
689
+ formatString = formatString.replace(param.placeholder, '{}');
690
+ formatArgs.push(param.rustName);
691
+ }
692
+ return {
693
+ formatString: `format!("${formatString}", ${formatArgs.join(', ')})`,
694
+ formatArgs: formatArgs.join(', ')
695
+ };
696
+ }
697
+
698
+ /**
699
+ * Generates TypeScript function parameters for dynamic channel parameters
700
+ *
701
+ * @param {Array} channelParameters - Array of channel parameter objects
702
+ * @returns {string} TypeScript function parameter string
703
+ */
704
+ function generateTypeScriptChannelParameterArgs(channelParameters) {
705
+ if (!channelParameters || channelParameters.length === 0) {
706
+ return '';
707
+ }
708
+ return channelParameters.map(param => `${param.name}: string`).join(', ') + ', ';
709
+ }
710
+
711
+ /**
712
+ * Generates TypeScript template literal for dynamic channel resolution
713
+ *
714
+ * @param {string} channelAddress - Original channel address with parameters
715
+ * @param {Array} channelParameters - Array of channel parameter objects
716
+ * @returns {string} TypeScript template literal string
717
+ */
718
+ function generateTypeScriptChannelFormatting(channelAddress, channelParameters) {
719
+ if (!channelParameters || channelParameters.length === 0) {
720
+ return `'${channelAddress}'`;
721
+ }
722
+
723
+ // Replace parameter placeholders with template literal placeholders
724
+ let templateString = channelAddress;
725
+ for (const param of channelParameters) {
726
+ templateString = templateString.replace(param.placeholder, `\${${param.name}}`);
727
+ }
728
+ return `\`${templateString}\``;
729
+ }
730
+
731
+ /**
732
+ * Extracts server name from a $ref string
733
+ *
734
+ * @param {string} serverRef - Server reference like "#/servers/mqtt-server"
735
+ * @returns {string|null} Server name or null if invalid
736
+ */
737
+ function extractServerNameFromRef(serverRef) {
738
+ if (!serverRef || typeof serverRef !== 'string') return null;
739
+
740
+ // Handle $ref format: "#/servers/server-name"
741
+ const refMatch = serverRef.match(/^#\/servers\/(.+)$/);
742
+ if (refMatch) {
743
+ return refMatch[1];
744
+ }
745
+ return null;
746
+ }
747
+
748
+ /**
749
+ * Analyzes channel server restrictions from AsyncAPI specification
750
+ *
751
+ * @param {object} asyncapi - AsyncAPI specification object
752
+ * @returns {Array} Array of channel server mapping objects
753
+ */
754
+ function analyzeChannelServerMappings(asyncapi) {
755
+ const mappings = [];
756
+ try {
757
+ const channels = asyncapi.channels();
758
+ if (!channels) return mappings;
759
+ for (const channel of channels) {
760
+ const channelName = channel.id();
761
+ let allowedServers = null; // null means available on all servers
762
+
763
+ // Try to get servers from the channel object
764
+ let channelServers = null;
765
+
766
+ // Method 1: Try channel.servers() function
767
+ if (channel.servers && typeof channel.servers === 'function') {
768
+ try {
769
+ channelServers = channel.servers();
770
+ } catch (e) {
771
+ // Ignore errors and try other methods
772
+ }
773
+ }
774
+
775
+ // Method 2: Try channel._json.servers (raw JSON data)
776
+ if (!channelServers && channel._json && channel._json.servers) {
777
+ channelServers = channel._json.servers;
778
+ }
779
+
780
+ // Method 3: Try direct property access
781
+ if (!channelServers && channel.servers && Array.isArray(channel.servers)) {
782
+ channelServers = channel.servers;
783
+ }
784
+ if (channelServers && Array.isArray(channelServers) && channelServers.length > 0) {
785
+ // Extract server names from $ref strings
786
+ allowedServers = [];
787
+ for (const serverRef of channelServers) {
788
+ let serverName = null;
789
+
790
+ // Handle different ways the server reference might be provided
791
+ if (typeof serverRef === 'string') {
792
+ serverName = extractServerNameFromRef(serverRef);
793
+ } else if (serverRef && serverRef.$ref) {
794
+ serverName = extractServerNameFromRef(serverRef.$ref);
795
+ } else if (serverRef && typeof serverRef.id === 'function') {
796
+ serverName = serverRef.id();
797
+ } else if (serverRef && typeof serverRef.id === 'string') {
798
+ serverName = serverRef.id;
799
+ }
800
+ if (serverName) {
801
+ allowedServers.push(serverName);
802
+ }
803
+ }
804
+
805
+ // If no valid server names were extracted, treat as available on all servers
806
+ if (allowedServers.length === 0) {
807
+ allowedServers = null;
808
+ }
809
+ }
810
+ mappings.push({
811
+ channelName: channelName,
812
+ allowedServers: allowedServers,
813
+ rustChannelName: toRustIdentifier(channelName),
814
+ description: channel.description && channel.description() || ''
815
+ });
816
+ }
817
+ } catch (e) {
818
+ console.warn('Error analyzing channel server mappings:', e.message);
819
+ }
820
+ return mappings;
821
+ }
822
+
823
+ /**
824
+ * Checks if a channel is allowed on a specific server
825
+ *
826
+ * @param {string} channelName - Name of the channel
827
+ * @param {string} serverName - Name of the server
828
+ * @param {Array} channelMappings - Array of channel server mappings
829
+ * @returns {boolean} True if channel is allowed on the server
830
+ */
831
+ function isChannelAllowedOnServer(channelName, serverName, channelMappings) {
832
+ const mapping = channelMappings.find(m => m.channelName === channelName);
833
+ if (!mapping) {
834
+ // If no mapping found, assume channel is allowed on all servers
835
+ return true;
836
+ }
837
+
838
+ // If allowedServers is null, channel is available on all servers
839
+ if (mapping.allowedServers === null) {
840
+ return true;
841
+ }
842
+
843
+ // Check if server is in the allowed list
844
+ return mapping.allowedServers.includes(serverName);
845
+ }
846
+ ;// ../common/src/security-utils.js
847
+ /**
848
+ * Security analysis utilities for AsyncAPI template generation
849
+ *
850
+ * This module provides functions for analyzing security requirements and
851
+ * authentication schemes across different AsyncAPI templates.
852
+ */
853
+
854
+ /**
855
+ * Analyzes operation security requirements from AsyncAPI specification
856
+ *
857
+ * @param {object} operation - AsyncAPI operation object
858
+ * @returns {object} Security analysis result
859
+ */
860
+ function analyzeOperationSecurity(operation) {
861
+ try {
862
+ // Check AsyncAPI security field
863
+ const security = operation.security && operation.security();
864
+ if (security && Array.isArray(security) && security.length > 0) {
865
+ return {
866
+ hasSecurityRequirements: true,
867
+ securitySchemes: security,
868
+ requiresAuthentication: true
869
+ };
870
+ }
871
+
872
+ // Check if operation has security defined in AsyncAPI spec
873
+ const operationJson = operation._json || operation;
874
+ if (operationJson.security && Array.isArray(operationJson.security) && operationJson.security.length > 0) {
875
+ return {
876
+ hasSecurityRequirements: true,
877
+ securitySchemes: operationJson.security,
878
+ requiresAuthentication: true
879
+ };
880
+ }
881
+ return {
882
+ hasSecurityRequirements: false,
883
+ securitySchemes: [],
884
+ requiresAuthentication: false
885
+ };
886
+ } catch (e) {
887
+ return {
888
+ hasSecurityRequirements: false,
889
+ securitySchemes: [],
890
+ requiresAuthentication: false
891
+ };
892
+ }
893
+ }
894
+
895
+ /**
896
+ * Checks if an operation has security requirements
897
+ *
898
+ * @param {object} operation - AsyncAPI operation object
899
+ * @returns {boolean} True if operation has security requirements
900
+ */
901
+ function operationHasSecurity(operation) {
902
+ const analysis = analyzeOperationSecurity(operation);
903
+ return analysis.hasSecurityRequirements;
904
+ }
905
+
906
+ /**
907
+ * Alias for operationHasSecurity for TypeScript templates
908
+ *
909
+ * @param {object} operation - AsyncAPI operation object
910
+ * @returns {boolean} True if operation requires authentication
911
+ */
912
+ function operationRequiresAuth(operation) {
913
+ return operationHasSecurity(operation);
914
+ }
915
+
916
+ /**
917
+ * Checks if the AsyncAPI specification has security schemes defined
918
+ *
919
+ * @param {object} asyncapi - AsyncAPI specification object
920
+ * @param {boolean} enableAuth - Whether auth feature is enabled (optional, defaults to true)
921
+ * @returns {boolean} True if security schemes are present and auth is enabled
922
+ */
923
+ function hasSecuritySchemes(asyncapi, enableAuth = true) {
924
+ if (!enableAuth) return false;
925
+ try {
926
+ const components = asyncapi.components();
927
+ if (!components) return false;
928
+ const securitySchemes = components.securitySchemes();
929
+ return securitySchemes && Object.keys(securitySchemes).length > 0;
930
+ } catch (e) {
931
+ return false;
932
+ }
933
+ }
934
+
935
+ /**
936
+ * Get security scheme type from AsyncAPI security scheme definition
937
+ *
938
+ * @param {object} securityScheme - AsyncAPI security scheme object
939
+ * @returns {string} Security scheme type ('jwt', 'basic', 'apikey', etc.)
940
+ */
941
+ function getSecuritySchemeType(securityScheme) {
942
+ try {
943
+ if (securityScheme.type && typeof securityScheme.type === 'function') {
944
+ return securityScheme.type();
945
+ }
946
+ if (securityScheme.type) {
947
+ return securityScheme.type;
948
+ }
949
+ if (securityScheme._json && securityScheme._json.type) {
950
+ return securityScheme._json.type;
951
+ }
952
+ return 'unknown';
953
+ } catch (e) {
954
+ return 'unknown';
955
+ }
956
+ }
957
+
958
+ /**
959
+ * Gets the security scheme name from a security scheme object
960
+ *
961
+ * @param {object} securityScheme - AsyncAPI security scheme object
962
+ * @returns {string} Security scheme name
963
+ */
964
+ function getSecuritySchemeName(securityScheme) {
965
+ try {
966
+ if (securityScheme.name && typeof securityScheme.name === 'function') {
967
+ return securityScheme.name();
968
+ }
969
+ if (securityScheme.name) {
970
+ return securityScheme.name;
971
+ }
972
+ if (securityScheme._json && securityScheme._json.name) {
973
+ return securityScheme._json.name;
974
+ }
975
+ return 'unknown';
976
+ } catch (e) {
977
+ return 'unknown';
978
+ }
979
+ }
980
+
981
+ /**
982
+ * Gets the security scheme location (header, query, cookie) for API key schemes
983
+ *
984
+ * @param {object} securityScheme - AsyncAPI security scheme object
985
+ * @returns {string} Security scheme location ('header', 'query', 'cookie')
986
+ */
987
+ function getSecuritySchemeLocation(securityScheme) {
988
+ try {
989
+ if (securityScheme.in && typeof securityScheme.in === 'function') {
990
+ return securityScheme.in();
991
+ }
992
+ if (securityScheme.in) {
993
+ return securityScheme.in;
994
+ }
995
+ if (securityScheme._json && securityScheme._json.in) {
996
+ return securityScheme._json.in;
997
+ }
998
+ return 'header'; // Default to header
999
+ } catch (e) {
1000
+ return 'header';
1001
+ }
1002
+ }
1003
+
1004
+ /**
1005
+ * Extract security requirements for all operations in the AsyncAPI spec
1006
+ *
1007
+ * @param {object} asyncapi - AsyncAPI specification object
1008
+ * @returns {object} Map of operation names to their security requirements
1009
+ */
1010
+ function extractOperationSecurityMap(asyncapi) {
1011
+ const securityMap = {};
1012
+ try {
1013
+ const operations = asyncapi.operations && asyncapi.operations();
1014
+ if (operations) {
1015
+ // Handle AsyncAPI parser collection - use .all() method to get array
1016
+ const operationArray = operations.all ? operations.all() : Object.values(operations);
1017
+ operationArray.forEach(operation => {
1018
+ // Get operation ID
1019
+ let operationId = null;
1020
+ if (operation._meta && operation._meta.id) {
1021
+ operationId = operation._meta.id;
1022
+ } else if (operation.id && typeof operation.id === 'function') {
1023
+ operationId = operation.id();
1024
+ } else if (operation.id) {
1025
+ operationId = operation.id;
1026
+ }
1027
+ if (operationId) {
1028
+ const securityAnalysis = analyzeOperationSecurity(operation);
1029
+ securityMap[operationId] = securityAnalysis;
1030
+ }
1031
+ });
1032
+ }
1033
+ } catch (e) {
1034
+ console.warn('Error extracting operation security map:', e.message);
1035
+ }
1036
+ return securityMap;
1037
+ }
1038
+
1039
+ /**
1040
+ * Gets all security schemes from the AsyncAPI specification
1041
+ *
1042
+ * @param {object} asyncapi - AsyncAPI specification object
1043
+ * @returns {object} Map of security scheme names to their definitions
1044
+ */
1045
+ function getAllSecuritySchemes(asyncapi) {
1046
+ try {
1047
+ const components = asyncapi.components();
1048
+ if (!components) return {};
1049
+ const securitySchemes = components.securitySchemes();
1050
+ if (!securitySchemes) return {};
1051
+
1052
+ // Convert to plain object if it's a collection
1053
+ if (typeof securitySchemes === 'object' && securitySchemes.all) {
1054
+ const schemes = {};
1055
+ const schemeArray = securitySchemes.all();
1056
+ schemeArray.forEach(scheme => {
1057
+ const name = scheme.id ? scheme.id() : 'unknown';
1058
+ schemes[name] = scheme;
1059
+ });
1060
+ return schemes;
1061
+ }
1062
+ return securitySchemes;
1063
+ } catch (e) {
1064
+ console.warn('Error extracting security schemes:', e.message);
1065
+ return {};
1066
+ }
1067
+ }
1068
+
1069
+ /**
1070
+ * Checks if a security scheme is of a specific type
1071
+ *
1072
+ * @param {object} securityScheme - AsyncAPI security scheme object
1073
+ * @param {string} expectedType - Expected security scheme type
1074
+ * @returns {boolean} True if the scheme matches the expected type
1075
+ */
1076
+ function isSecuritySchemeType(securityScheme, expectedType) {
1077
+ const actualType = getSecuritySchemeType(securityScheme);
1078
+ return actualType.toLowerCase() === expectedType.toLowerCase();
1079
+ }
1080
+
1081
+ /**
1082
+ * Gets the default port for a given protocol
1083
+ *
1084
+ * @param {string} protocol - Protocol name
1085
+ * @returns {number} Default port number
1086
+ */
1087
+ function getDefaultPort(protocol) {
1088
+ switch (protocol === null || protocol === void 0 ? void 0 : protocol.toLowerCase()) {
1089
+ case 'http':
1090
+ return 80;
1091
+ case 'https':
1092
+ return 443;
1093
+ case 'ws':
1094
+ case 'websocket':
1095
+ return 80;
1096
+ case 'wss':
1097
+ case 'websockets':
1098
+ return 443;
1099
+ case 'mqtt':
1100
+ return 1883;
1101
+ case 'mqtts':
1102
+ return 8883;
1103
+ case 'amqp':
1104
+ return 5672;
1105
+ case 'amqps':
1106
+ return 5671;
1107
+ case 'kafka':
1108
+ return 9092;
1109
+ case 'nats':
1110
+ return 4222;
1111
+ default:
1112
+ return 8080;
1113
+ }
1114
+ }
1115
+
1116
+ /**
1117
+ * Validates that all server references in channels exist in the servers section
1118
+ *
1119
+ * @param {object} asyncapi - AsyncAPI specification object
1120
+ * @returns {object} Validation result with errors if any
1121
+ */
1122
+ function validateChannelServerReferences(asyncapi) {
1123
+ const result = {
1124
+ valid: true,
1125
+ errors: []
1126
+ };
1127
+ try {
1128
+ // Try multiple ways to get servers
1129
+ let servers = null;
1130
+ let serverNames = [];
1131
+
1132
+ // Method 1: Try asyncapi.servers() function
1133
+ if (asyncapi.servers && typeof asyncapi.servers === 'function') {
1134
+ try {
1135
+ servers = asyncapi.servers();
1136
+ if (servers) {
1137
+ // Check if this is a collection object (has iterator methods)
1138
+ if (typeof servers[Symbol.iterator] === 'function') {
1139
+ // It's iterable - iterate through the servers
1140
+ serverNames = [];
1141
+ for (const server of servers) {
1142
+ const serverName = server.id && typeof server.id === 'function' ? server.id() : server.id;
1143
+ if (serverName) {
1144
+ serverNames.push(serverName);
1145
+ }
1146
+ }
1147
+ } else {
1148
+ // It's a plain object - use Object.keys
1149
+ serverNames = Object.keys(servers);
1150
+ }
1151
+ }
1152
+ } catch (e) {
1153
+ // Ignore and try other methods
1154
+ }
1155
+ }
1156
+
1157
+ // Method 2: Try asyncapi._json.servers (raw JSON data)
1158
+ if (serverNames.length === 0 && asyncapi._json && asyncapi._json.servers) {
1159
+ servers = asyncapi._json.servers;
1160
+ serverNames = Object.keys(servers);
1161
+ }
1162
+
1163
+ // Method 3: Try direct property access
1164
+ if (serverNames.length === 0 && asyncapi.servers && typeof asyncapi.servers === 'object') {
1165
+ servers = asyncapi.servers;
1166
+ serverNames = Object.keys(servers);
1167
+ }
1168
+
1169
+ // Method 4: Try json() method if available
1170
+ if (serverNames.length === 0 && asyncapi.json && typeof asyncapi.json === 'function') {
1171
+ try {
1172
+ const jsonDoc = asyncapi.json();
1173
+ if (jsonDoc && jsonDoc.servers) {
1174
+ servers = jsonDoc.servers;
1175
+ serverNames = Object.keys(servers);
1176
+ }
1177
+ } catch (e) {
1178
+ // Ignore and try other methods
1179
+ }
1180
+ }
1181
+
1182
+ // Note: We'll skip channel validation here to avoid circular dependency
1183
+ // This function can be enhanced later if needed
1184
+ console.warn('Channel server validation skipped to avoid circular dependency');
1185
+ } catch (e) {
1186
+ result.valid = false;
1187
+ result.errors.push({
1188
+ message: `Error validating channel server references: ${e.message}`
1189
+ });
1190
+ }
1191
+ return result;
1192
+ }
1193
+ ;// ../common/src/template-utils.js
1194
+ /**
1195
+ * Template parameter utilities for AsyncAPI template generation
1196
+ *
1197
+ * This module provides functions for handling template parameters and
1198
+ * common template operations across different AsyncAPI templates.
1199
+ */
1200
+
1201
+
1202
+
1203
+ /**
1204
+ * Checks if a parameter contains unresolved template variables
1205
+ *
1206
+ * @param {string} value - Parameter value to check
1207
+ * @returns {boolean} True if the value contains template variables
1208
+ */
1209
+ function isTemplateVariable(value) {
1210
+ return typeof value === 'string' && value.includes('{{') && value.includes('}}');
1211
+ }
1212
+
1213
+ /**
1214
+ * Extracts information from AsyncAPI specification with fallbacks
1215
+ *
1216
+ * @param {object} asyncapi - AsyncAPI specification object
1217
+ * @returns {object} Object with title, version, and description
1218
+ */
1219
+ function extractAsyncApiInfo(asyncapi) {
1220
+ let title, version, description;
1221
+ try {
1222
+ const info = asyncapi.info();
1223
+ title = info.title();
1224
+ version = info.version();
1225
+ description = info.description();
1226
+ } catch (error) {
1227
+ title = 'UnknownAPI';
1228
+ version = '1.0.0';
1229
+ description = 'Generated AsyncAPI client';
1230
+ }
1231
+ return {
1232
+ title,
1233
+ version,
1234
+ description
1235
+ };
1236
+ }
1237
+
1238
+ /**
1239
+ * Resolves template parameters with fallbacks based on AsyncAPI info
1240
+ *
1241
+ * @param {object} params - Template parameters
1242
+ * @param {object} asyncApiInfo - AsyncAPI info object from extractAsyncApiInfo
1243
+ * @returns {object} Resolved parameters
1244
+ */
1245
+ function resolveTemplateParameters(params, asyncApiInfo) {
1246
+ const {
1247
+ title,
1248
+ version
1249
+ } = asyncApiInfo;
1250
+
1251
+ // Resolve parameters, falling back to extracted values if parameters contain template variables
1252
+ const clientName = params.clientName && !isTemplateVariable(params.clientName) ? params.clientName : `${toPascalCase(title)}Client`;
1253
+ const packageName = params.packageName && !isTemplateVariable(params.packageName) ? params.packageName : `${toKebabCase(title)}-client`;
1254
+ const packageVersion = params.packageVersion && !isTemplateVariable(params.packageVersion) ? params.packageVersion : version;
1255
+ const license = params.license && !isTemplateVariable(params.license) ? params.license : 'Apache-2.0';
1256
+ const author = params.author && !isTemplateVariable(params.author) ? params.author : 'AsyncAPI Generator';
1257
+ return {
1258
+ clientName,
1259
+ packageName,
1260
+ packageVersion,
1261
+ license,
1262
+ author
1263
+ };
1264
+ }
1265
+
1266
+ /**
1267
+ * Generates package.json content for Node.js templates
1268
+ *
1269
+ * @param {object} resolvedParams - Resolved template parameters
1270
+ * @param {object} asyncApiInfo - AsyncAPI info object
1271
+ * @param {object} options - Additional options for package.json generation
1272
+ * @returns {object} Package.json object
1273
+ */
1274
+ function generatePackageJson(resolvedParams, asyncApiInfo, options = {}) {
1275
+ const {
1276
+ title,
1277
+ description
1278
+ } = asyncApiInfo;
1279
+ const {
1280
+ packageName,
1281
+ packageVersion,
1282
+ license,
1283
+ author
1284
+ } = resolvedParams;
1285
+ const basePackage = {
1286
+ name: packageName,
1287
+ version: packageVersion,
1288
+ description: `${description || title} - AsyncAPI Client`,
1289
+ author: author,
1290
+ license: license,
1291
+ keywords: ['asyncapi', 'client', title.toLowerCase().replace(/[^a-z0-9]/g, '-'), ...(options.additionalKeywords || [])]
1292
+ };
1293
+
1294
+ // Merge with additional options
1295
+ return {
1296
+ ...basePackage,
1297
+ ...options.additionalFields
1298
+ };
1299
+ }
1300
+
1301
+ /**
1302
+ * Generates README.md content for templates
1303
+ *
1304
+ * @param {object} resolvedParams - Resolved template parameters
1305
+ * @param {object} asyncApiInfo - AsyncAPI info object
1306
+ * @param {object} asyncapi - AsyncAPI specification object
1307
+ * @param {object} options - Additional options for README generation
1308
+ * @returns {string} README.md content
1309
+ */
1310
+ function generateReadmeContent(resolvedParams, asyncApiInfo, asyncapi, options = {}) {
1311
+ const {
1312
+ title,
1313
+ description
1314
+ } = asyncApiInfo;
1315
+ const {
1316
+ packageName,
1317
+ packageVersion,
1318
+ license
1319
+ } = resolvedParams;
1320
+ const sections = {
1321
+ title: `# ${title}`,
1322
+ description: description || 'Generated AsyncAPI client',
1323
+ overview: options.overview || 'This client provides type-safe access to your AsyncAPI service.',
1324
+ installation: options.installation || `\`\`\`bash\nnpm install ${packageName}\n\`\`\``,
1325
+ usage: options.usage || '// Usage examples will be added here',
1326
+ metadata: `## Generated from AsyncAPI
1327
+
1328
+ - **AsyncAPI Version**: ${asyncapi.version()}
1329
+ - **Generated**: ${new Date().toISOString()}
1330
+ - **Title**: ${title}
1331
+ - **Version**: ${packageVersion}`,
1332
+ license: `## License
1333
+
1334
+ ${license}`
1335
+ };
1336
+
1337
+ // Allow overriding sections
1338
+ const finalSections = {
1339
+ ...sections,
1340
+ ...options.sections
1341
+ };
1342
+ return Object.values(finalSections).join('\n\n');
1343
+ }
1344
+
1345
+ /**
1346
+ * Validates template parameters
1347
+ *
1348
+ * @param {object} params - Template parameters to validate
1349
+ * @param {Array<string>} requiredParams - List of required parameter names
1350
+ * @returns {object} Validation result with errors if any
1351
+ */
1352
+ function validateTemplateParameters(params, requiredParams = []) {
1353
+ const result = {
1354
+ valid: true,
1355
+ errors: [],
1356
+ warnings: []
1357
+ };
1358
+
1359
+ // Check required parameters
1360
+ for (const paramName of requiredParams) {
1361
+ if (!params[paramName] || isTemplateVariable(params[paramName])) {
1362
+ result.valid = false;
1363
+ result.errors.push(`Required parameter '${paramName}' is missing or contains unresolved template variables`);
1364
+ }
1365
+ }
1366
+
1367
+ // Check for common issues
1368
+ if (params.packageName && !/^[a-z0-9-]+$/.test(params.packageName)) {
1369
+ result.warnings.push('Package name should only contain lowercase letters, numbers, and hyphens');
1370
+ }
1371
+ if (params.packageVersion && !/^\d+\.\d+\.\d+/.test(params.packageVersion)) {
1372
+ result.warnings.push('Package version should follow semantic versioning (e.g., 1.0.0)');
1373
+ }
1374
+ return result;
1375
+ }
1376
+
1377
+ /**
1378
+ * Generates TypeScript configuration for TypeScript templates
1379
+ *
1380
+ * @param {object} options - TypeScript configuration options
1381
+ * @returns {object} TypeScript configuration object
1382
+ */
1383
+ function generateTypeScriptConfig(options = {}) {
1384
+ const defaultConfig = {
1385
+ compilerOptions: {
1386
+ target: 'ES2020',
1387
+ module: 'ES2020',
1388
+ lib: ['ES2020', 'DOM'],
1389
+ outDir: './dist',
1390
+ rootDir: './src',
1391
+ strict: true,
1392
+ esModuleInterop: true,
1393
+ skipLibCheck: true,
1394
+ forceConsistentCasingInFileNames: true,
1395
+ declaration: true,
1396
+ declarationMap: true,
1397
+ sourceMap: true,
1398
+ moduleResolution: 'bundler',
1399
+ allowSyntheticDefaultImports: true,
1400
+ experimentalDecorators: true,
1401
+ emitDecoratorMetadata: true,
1402
+ resolveJsonModule: true,
1403
+ typeRoots: ['node_modules/@types']
1404
+ },
1405
+ include: ['src/**/*'],
1406
+ exclude: ['node_modules', 'dist', '**/*.test.ts', '**/*.spec.ts']
1407
+ };
1408
+
1409
+ // Merge with custom options
1410
+ return {
1411
+ ...defaultConfig,
1412
+ compilerOptions: {
1413
+ ...defaultConfig.compilerOptions,
1414
+ ...options.compilerOptions
1415
+ },
1416
+ include: options.include || defaultConfig.include,
1417
+ exclude: options.exclude || defaultConfig.exclude
1418
+ };
1419
+ }
1420
+
1421
+ /**
1422
+ * Generates ESLint configuration for JavaScript/TypeScript templates
1423
+ *
1424
+ * @param {object} options - ESLint configuration options
1425
+ * @returns {object} ESLint configuration object
1426
+ */
1427
+ function generateEslintConfig(options = {}) {
1428
+ const isTypeScript = options.typescript || false;
1429
+ const baseConfig = {
1430
+ env: {
1431
+ node: true,
1432
+ es2021: true
1433
+ },
1434
+ extends: ['eslint:recommended'],
1435
+ parserOptions: {
1436
+ ecmaVersion: 12,
1437
+ sourceType: 'module'
1438
+ },
1439
+ rules: {
1440
+ 'indent': ['error', 4],
1441
+ 'linebreak-style': ['error', 'unix'],
1442
+ 'quotes': ['error', 'single'],
1443
+ 'semi': ['error', 'always']
1444
+ }
1445
+ };
1446
+ if (isTypeScript) {
1447
+ baseConfig.extends.push('@typescript-eslint/recommended');
1448
+ baseConfig.parser = '@typescript-eslint/parser';
1449
+ baseConfig.plugins = ['@typescript-eslint'];
1450
+ }
1451
+
1452
+ // Merge with custom options
1453
+ return {
1454
+ ...baseConfig,
1455
+ ...options.additionalConfig
1456
+ };
1457
+ }
1458
+
1459
+ /**
1460
+ * Formats a date for use in generated files
1461
+ *
1462
+ * @param {Date} date - Date to format (defaults to current date)
1463
+ * @returns {string} Formatted date string
1464
+ */
1465
+ function formatGenerationDate(date = new Date()) {
1466
+ return date.toISOString();
1467
+ }
1468
+
1469
+ /**
1470
+ * Generates a comment header for generated files
1471
+ *
1472
+ * @param {object} options - Header options
1473
+ * @returns {string} Comment header
1474
+ */
1475
+ function generateFileHeader(options = {}) {
1476
+ const {
1477
+ title = 'Generated AsyncAPI File',
1478
+ description = 'This file was automatically generated from an AsyncAPI specification.',
1479
+ generator = 'AsyncAPI Generator',
1480
+ date = new Date(),
1481
+ warning = 'Do not modify this file directly.'
1482
+ } = options;
1483
+ return `/**
1484
+ * ${title}
1485
+ *
1486
+ * ${description}
1487
+ *
1488
+ * Generated by: ${generator}
1489
+ * Generated on: ${formatGenerationDate(date)}
1490
+ *
1491
+ * WARNING: ${warning}
1492
+ */`;
1493
+ }
1494
+ ;// ../common/src/operation-utils.js
1495
+ /**
1496
+ * Operation processing utilities for AsyncAPI template generation
1497
+ *
1498
+ * This module provides functions for extracting and processing operation information
1499
+ * from AsyncAPI specifications, handling different operation types and patterns.
1500
+ */
1501
+
1502
+
1503
+
1504
+ /**
1505
+ * Gets the operation name from an operation object
1506
+ *
1507
+ * @param {object} operation - AsyncAPI operation object
1508
+ * @returns {string|null} Operation name or null if not found
1509
+ */
1510
+ function getOperationName(operation) {
1511
+ if (!operation) return null;
1512
+ try {
1513
+ // Try AsyncAPI 3.x format first - check _meta and _json properties
1514
+ if (operation._meta && operation._meta.id) {
1515
+ return operation._meta.id;
1516
+ }
1517
+ if (operation._json && operation._json['x-parser-operation-id']) {
1518
+ return operation._json['x-parser-operation-id'];
1519
+ }
1520
+
1521
+ // Try different ways to get the operation name
1522
+ if (operation.id && typeof operation.id === 'function') {
1523
+ return operation.id();
1524
+ }
1525
+ if (operation.id && typeof operation.id === 'string') {
1526
+ return operation.id;
1527
+ }
1528
+ if (operation.operationId && typeof operation.operationId === 'function') {
1529
+ return operation.operationId();
1530
+ }
1531
+ if (operation.operationId && typeof operation.operationId === 'string') {
1532
+ return operation.operationId;
1533
+ }
1534
+ return null;
1535
+ } catch (e) {
1536
+ return null;
1537
+ }
1538
+ }
1539
+
1540
+ /**
1541
+ * Gets the operation action (send/receive/publish/subscribe)
1542
+ *
1543
+ * @param {object} operation - AsyncAPI operation object
1544
+ * @returns {string} Operation action
1545
+ */
1546
+ function getOperationAction(operation) {
1547
+ if (!operation) return 'unknown';
1548
+ try {
1549
+ if (operation.action && typeof operation.action === 'function') {
1550
+ return operation.action();
1551
+ }
1552
+ if (operation.action && typeof operation.action === 'string') {
1553
+ return operation.action;
1554
+ }
1555
+ if (operation._json && operation._json.action) {
1556
+ return operation._json.action;
1557
+ }
1558
+
1559
+ // Fallback based on operation type
1560
+ if (operation.isSend && typeof operation.isSend === 'function' && operation.isSend()) {
1561
+ return 'send';
1562
+ }
1563
+ if (operation.isReceive && typeof operation.isReceive === 'function' && operation.isReceive()) {
1564
+ return 'receive';
1565
+ }
1566
+ return 'unknown';
1567
+ } catch (e) {
1568
+ return 'unknown';
1569
+ }
1570
+ }
1571
+
1572
+ /**
1573
+ * Gets the channel associated with an operation
1574
+ *
1575
+ * @param {object} operation - AsyncAPI operation object
1576
+ * @returns {object|null} Channel object or null if not found
1577
+ */
1578
+ function getOperationChannel(operation) {
1579
+ if (!operation) return null;
1580
+ try {
1581
+ if (operation.channel && typeof operation.channel === 'function') {
1582
+ return operation.channel();
1583
+ }
1584
+ if (operation.channel && typeof operation.channel === 'object') {
1585
+ return operation.channel;
1586
+ }
1587
+ return null;
1588
+ } catch (e) {
1589
+ return null;
1590
+ }
1591
+ }
1592
+
1593
+ /**
1594
+ * Gets the messages associated with an operation
1595
+ *
1596
+ * @param {object} operation - AsyncAPI operation object
1597
+ * @returns {Array} Array of message objects
1598
+ */
1599
+ function getOperationMessages(operation) {
1600
+ if (!operation) return [];
1601
+ try {
1602
+ const messages = [];
1603
+
1604
+ // Try to get messages from the operation
1605
+ if (operation.messages && typeof operation.messages === 'function') {
1606
+ const operationMessages = operation.messages();
1607
+ if (operationMessages && Array.isArray(operationMessages)) {
1608
+ messages.push(...operationMessages);
1609
+ } else if (operationMessages && typeof operationMessages === 'object') {
1610
+ // Handle collection object
1611
+ if (operationMessages.all && typeof operationMessages.all === 'function') {
1612
+ messages.push(...operationMessages.all());
1613
+ } else {
1614
+ messages.push(...Object.values(operationMessages));
1615
+ }
1616
+ }
1617
+ } else if (operation.messages && Array.isArray(operation.messages)) {
1618
+ messages.push(...operation.messages);
1619
+ }
1620
+
1621
+ // Try to get message from _json
1622
+ if (messages.length === 0 && operation._json && operation._json.message) {
1623
+ if (Array.isArray(operation._json.message)) {
1624
+ messages.push(...operation._json.message);
1625
+ } else {
1626
+ messages.push(operation._json.message);
1627
+ }
1628
+ }
1629
+ return messages;
1630
+ } catch (e) {
1631
+ return [];
1632
+ }
1633
+ }
1634
+
1635
+ /**
1636
+ * Checks if an operation is a send operation
1637
+ *
1638
+ * @param {object} operation - AsyncAPI operation object
1639
+ * @returns {boolean} True if operation is a send operation
1640
+ */
1641
+ function isOperationSend(operation) {
1642
+ const action = getOperationAction(operation);
1643
+ return action === 'send' || action === 'publish';
1644
+ }
1645
+
1646
+ /**
1647
+ * Checks if an operation is a receive operation
1648
+ *
1649
+ * @param {object} operation - AsyncAPI operation object
1650
+ * @returns {boolean} True if operation is a receive operation
1651
+ */
1652
+ function isOperationReceive(operation) {
1653
+ const action = getOperationAction(operation);
1654
+ return action === 'receive' || action === 'subscribe';
1655
+ }
1656
+
1657
+ /**
1658
+ * Gets the Rust function name for an operation
1659
+ *
1660
+ * @param {object} operation - AsyncAPI operation object
1661
+ * @returns {string} Rust function name
1662
+ */
1663
+ function getOperationRustFunctionName(operation) {
1664
+ const operationName = getOperationName(operation);
1665
+ if (!operationName) return 'unknown_operation';
1666
+ return toRustIdentifier(operationName);
1667
+ }
1668
+
1669
+ /**
1670
+ * Gets the TypeScript method name for an operation
1671
+ *
1672
+ * @param {object} operation - AsyncAPI operation object
1673
+ * @returns {string} TypeScript method name
1674
+ */
1675
+ function getOperationTypeScriptMethodName(operation) {
1676
+ const operationName = getOperationName(operation);
1677
+ if (!operationName) return 'unknownOperation';
1678
+
1679
+ // Convert to camelCase for TypeScript
1680
+ return operationName.split(/[-_\s]+/).map((part, index) => {
1681
+ if (index === 0) {
1682
+ return part.toLowerCase();
1683
+ }
1684
+ return part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
1685
+ }).join('');
1686
+ }
1687
+
1688
+ /**
1689
+ * Gets the operation description
1690
+ *
1691
+ * @param {object} operation - AsyncAPI operation object
1692
+ * @returns {string} Operation description
1693
+ */
1694
+ function getOperationDescription(operation) {
1695
+ if (!operation) return '';
1696
+ try {
1697
+ if (operation.description && typeof operation.description === 'function') {
1698
+ return operation.description() || '';
1699
+ }
1700
+ if (operation.description && typeof operation.description === 'string') {
1701
+ return operation.description;
1702
+ }
1703
+ if (operation._json && operation._json.description) {
1704
+ return operation._json.description;
1705
+ }
1706
+ return '';
1707
+ } catch (e) {
1708
+ return '';
1709
+ }
1710
+ }
1711
+
1712
+ /**
1713
+ * Gets the operation summary
1714
+ *
1715
+ * @param {object} operation - AsyncAPI operation object
1716
+ * @returns {string} Operation summary
1717
+ */
1718
+ function getOperationSummary(operation) {
1719
+ if (!operation) return '';
1720
+ try {
1721
+ if (operation.summary && typeof operation.summary === 'function') {
1722
+ return operation.summary() || '';
1723
+ }
1724
+ if (operation.summary && typeof operation.summary === 'string') {
1725
+ return operation.summary;
1726
+ }
1727
+ if (operation._json && operation._json.summary) {
1728
+ return operation._json.summary;
1729
+ }
1730
+ return '';
1731
+ } catch (e) {
1732
+ return '';
1733
+ }
1734
+ }
1735
+
1736
+ /**
1737
+ * Extracts all operations from an AsyncAPI specification
1738
+ *
1739
+ * @param {object} asyncapi - AsyncAPI specification object
1740
+ * @returns {Array} Array of operation objects with metadata
1741
+ */
1742
+ function extractAllOperations(asyncapi) {
1743
+ const operations = [];
1744
+ try {
1745
+ const asyncApiOperations = asyncapi.operations && asyncapi.operations();
1746
+ if (asyncApiOperations) {
1747
+ // Handle AsyncAPI parser collection - use .all() method to get array
1748
+ const operationArray = asyncApiOperations.all ? asyncApiOperations.all() : Object.values(asyncApiOperations);
1749
+ operationArray.forEach(operation => {
1750
+ const operationName = getOperationName(operation);
1751
+ if (operationName) {
1752
+ const channel = getOperationChannel(operation);
1753
+ const messages = getOperationMessages(operation);
1754
+ operations.push({
1755
+ name: operationName,
1756
+ operation: operation,
1757
+ action: getOperationAction(operation),
1758
+ channel: channel,
1759
+ messages: messages,
1760
+ description: getOperationDescription(operation),
1761
+ summary: getOperationSummary(operation),
1762
+ rustFunctionName: getOperationRustFunctionName(operation),
1763
+ typeScriptMethodName: getOperationTypeScriptMethodName(operation),
1764
+ isSend: isOperationSend(operation),
1765
+ isReceive: isOperationReceive(operation)
1766
+ });
1767
+ }
1768
+ });
1769
+ }
1770
+ } catch (e) {
1771
+ console.warn('Error extracting operations:', e.message);
1772
+ }
1773
+ return operations;
1774
+ }
1775
+
1776
+ /**
1777
+ * Groups operations by their action type
1778
+ *
1779
+ * @param {Array} operations - Array of operation objects
1780
+ * @returns {object} Object with send and receive operation arrays
1781
+ */
1782
+ function groupOperationsByAction(operations) {
1783
+ const grouped = {
1784
+ send: [],
1785
+ receive: [],
1786
+ publish: [],
1787
+ subscribe: [],
1788
+ unknown: []
1789
+ };
1790
+ operations.forEach(operation => {
1791
+ const action = operation.action || 'unknown';
1792
+ if (grouped[action]) {
1793
+ grouped[action].push(operation);
1794
+ } else {
1795
+ grouped.unknown.push(operation);
1796
+ }
1797
+ });
1798
+ return grouped;
1799
+ }
1800
+
1801
+ /**
1802
+ * Gets the operation trait information
1803
+ *
1804
+ * @param {object} operation - AsyncAPI operation object
1805
+ * @returns {Array} Array of trait objects
1806
+ */
1807
+ function getOperationTraits(operation) {
1808
+ if (!operation) return [];
1809
+ try {
1810
+ const traits = [];
1811
+ if (operation.traits && typeof operation.traits === 'function') {
1812
+ const operationTraits = operation.traits();
1813
+ if (operationTraits && Array.isArray(operationTraits)) {
1814
+ traits.push(...operationTraits);
1815
+ }
1816
+ } else if (operation.traits && Array.isArray(operation.traits)) {
1817
+ traits.push(...operation.traits);
1818
+ }
1819
+
1820
+ // Try to get traits from _json
1821
+ if (traits.length === 0 && operation._json && operation._json.traits) {
1822
+ if (Array.isArray(operation._json.traits)) {
1823
+ traits.push(...operation._json.traits);
1824
+ }
1825
+ }
1826
+ return traits;
1827
+ } catch (e) {
1828
+ return [];
1829
+ }
1830
+ }
1831
+
1832
+ /**
1833
+ * Checks if an operation has any traits defined
1834
+ *
1835
+ * @param {object} operation - AsyncAPI operation object
1836
+ * @returns {boolean} True if operation has traits
1837
+ */
1838
+ function operationHasTraits(operation) {
1839
+ const traits = getOperationTraits(operation);
1840
+ return traits.length > 0;
1841
+ }
1842
+
1843
+ /**
1844
+ * Gets the operation tags
1845
+ *
1846
+ * @param {object} operation - AsyncAPI operation object
1847
+ * @returns {Array} Array of tag objects
1848
+ */
1849
+ function getOperationTags(operation) {
1850
+ if (!operation) return [];
1851
+ try {
1852
+ const tags = [];
1853
+ if (operation.tags && typeof operation.tags === 'function') {
1854
+ const operationTags = operation.tags();
1855
+ if (operationTags && Array.isArray(operationTags)) {
1856
+ tags.push(...operationTags);
1857
+ }
1858
+ } else if (operation.tags && Array.isArray(operation.tags)) {
1859
+ tags.push(...operation.tags);
1860
+ }
1861
+
1862
+ // Try to get tags from _json
1863
+ if (tags.length === 0 && operation._json && operation._json.tags) {
1864
+ if (Array.isArray(operation._json.tags)) {
1865
+ tags.push(...operation._json.tags);
1866
+ }
1867
+ }
1868
+ return tags;
1869
+ } catch (e) {
1870
+ return [];
1871
+ }
1872
+ }
1873
+
1874
+ /**
1875
+ * Generates operation handler name for server templates
1876
+ *
1877
+ * @param {object} operation - AsyncAPI operation object
1878
+ * @param {string} suffix - Optional suffix to add to handler name
1879
+ * @returns {string} Handler function name
1880
+ */
1881
+ function generateOperationHandlerName(operation, suffix = 'Handler') {
1882
+ const operationName = getOperationName(operation);
1883
+ if (!operationName) return `unknown${suffix}`;
1884
+ const pascalCaseName = toPascalCase(operationName);
1885
+ return `${pascalCaseName}${suffix}`;
1886
+ }
1887
+ ;// external "@asyncapi/generator-react-sdk"
1888
+
1889
+ ;// ../common/src/models-rust.js
1890
+ /* eslint-disable no-unused-vars */
1891
+
1892
+
1893
+ /**
1894
+ * Generate Rust models from AsyncAPI specification
1895
+ * This helper extracts the common schema processing logic used by both rust-server and rust-client templates
1896
+ *
1897
+ * @param {Object} asyncapi - AsyncAPI document
1898
+ * @param {Object} options - Generation options
1899
+ * @param {Function} options.toRustTypeName - Function to convert names to Rust type names
1900
+ * @param {Function} options.toRustFieldName - Function to convert names to Rust field names
1901
+ * @param {Function} options.toRustEnumVariantWithSerde - Function to convert enum variants with serde attributes
1902
+ * @param {boolean} options.includeAsyncApiTrait - Whether to include AsyncApiMessage trait implementations
1903
+ * @param {boolean} options.includeEnvelope - Whether to include MessageEnvelope (from envelope-rust.js)
1904
+ * @returns {Object} Generated models data and functions
1905
+ */
1906
+ function generateRustModels(asyncapi, options = {}) {
1907
+ const {
1908
+ toRustTypeName,
1909
+ toRustFieldName,
1910
+ toRustEnumVariantWithSerde,
1911
+ includeAsyncApiTrait = false,
1912
+ includeEnvelope = false
1913
+ } = options;
1914
+
1915
+ // Extract message schemas and build channel mapping
1916
+ const components = asyncapi.components();
1917
+ const messageSchemas = [];
1918
+ const componentSchemas = [];
1919
+ const messageToChannels = new Map();
1920
+ const generatedTypes = new Set();
1921
+ const nestedSchemas = new Map();
1922
+ const schemaRegistry = new Map();
1923
+
1924
+ // First, build channel to message mapping and extract inline message schemas
1925
+ if (asyncapi.channels) {
1926
+ const channels = asyncapi.channels();
1927
+ if (channels) {
1928
+ // Use proper iteration for AsyncAPI collection
1929
+ for (const channel of channels) {
1930
+ try {
1931
+ const channelName = channel.id();
1932
+
1933
+ // Handle AsyncAPI 3.x format - extract inline messages from channels
1934
+ if (channel.messages) {
1935
+ const messages = channel.messages();
1936
+ if (messages) {
1937
+ // Check if messages is an object with message names as keys
1938
+ if (typeof messages === 'object' && !Array.isArray(messages)) {
1939
+ // Iterate through message entries (messageName -> messageObject)
1940
+ Object.entries(messages).forEach(([messageName, message]) => {
1941
+ if (message && messageName) {
1942
+ let payload = null;
1943
+ let description = null;
1944
+
1945
+ // Get payload schema
1946
+ if (message.payload && typeof message.payload === 'function') {
1947
+ payload = message.payload();
1948
+ if (payload && payload.json && typeof payload.json === 'function') {
1949
+ payload = payload.json();
1950
+ }
1951
+ } else if (message.payload) {
1952
+ payload = message.payload;
1953
+ }
1954
+
1955
+ // Get description
1956
+ if (message.description && typeof message.description === 'function') {
1957
+ description = message.description();
1958
+ } else if (message.description) {
1959
+ description = message.description;
1960
+ }
1961
+
1962
+ // Add to channel mapping
1963
+ if (!messageToChannels.has(messageName)) {
1964
+ messageToChannels.set(messageName, []);
1965
+ }
1966
+ messageToChannels.get(messageName).push(channelName);
1967
+
1968
+ // Add to message schemas for inline messages
1969
+ messageSchemas.push({
1970
+ name: messageName,
1971
+ rustName: toRustTypeName(messageName),
1972
+ payload,
1973
+ rawPayload: payload,
1974
+ description,
1975
+ channels: [channelName]
1976
+ });
1977
+ }
1978
+ });
1979
+ } else {
1980
+ // Try iterating as a collection
1981
+ for (const message of messages) {
1982
+ if (message) {
1983
+ let messageName = null;
1984
+ let payload = null;
1985
+ let description = null;
1986
+
1987
+ // Get message name - try multiple approaches
1988
+ if (message._meta && message._meta.id) {
1989
+ messageName = message._meta.id;
1990
+ } else if (message._json && message._json['x-parser-message-name']) {
1991
+ messageName = message._json['x-parser-message-name'];
1992
+ } else if (message._json && message._json['x-parser-unique-object-id']) {
1993
+ messageName = message._json['x-parser-unique-object-id'];
1994
+ } else if (message.name && typeof message.name === 'function') {
1995
+ messageName = message.name();
1996
+ } else if (message.name) {
1997
+ messageName = message.name;
1998
+ } else if (message.$ref) {
1999
+ messageName = message.$ref.split('/').pop();
2000
+ }
2001
+
2002
+ // Get payload schema
2003
+ if (message.payload && typeof message.payload === 'function') {
2004
+ payload = message.payload();
2005
+ if (payload && payload.json && typeof payload.json === 'function') {
2006
+ payload = payload.json();
2007
+ }
2008
+ } else if (message.payload) {
2009
+ payload = message.payload;
2010
+ }
2011
+
2012
+ // Get description
2013
+ if (message.description && typeof message.description === 'function') {
2014
+ description = message.description();
2015
+ } else if (message.description) {
2016
+ description = message.description;
2017
+ }
2018
+ if (messageName) {
2019
+ // Add to channel mapping
2020
+ if (!messageToChannels.has(messageName)) {
2021
+ messageToChannels.set(messageName, []);
2022
+ }
2023
+ messageToChannels.get(messageName).push(channelName);
2024
+
2025
+ // Add to message schemas for inline messages
2026
+ messageSchemas.push({
2027
+ name: messageName,
2028
+ rustName: toRustTypeName(messageName),
2029
+ payload,
2030
+ rawPayload: payload,
2031
+ description,
2032
+ channels: [channelName]
2033
+ });
2034
+ }
2035
+ }
2036
+ }
2037
+ }
2038
+ }
2039
+ }
2040
+
2041
+ // Handle AsyncAPI 2.x format
2042
+ if (channel.subscribe && channel.subscribe()) {
2043
+ const message = channel.subscribe().message();
2044
+ if (message) {
2045
+ let messageName = null;
2046
+ if (message.$ref) {
2047
+ messageName = message.$ref.split('/').pop();
2048
+ } else if (message.name) {
2049
+ messageName = typeof message.name === 'function' ? message.name() : message.name;
2050
+ }
2051
+ if (messageName) {
2052
+ if (!messageToChannels.has(messageName)) {
2053
+ messageToChannels.set(messageName, []);
2054
+ }
2055
+ messageToChannels.get(messageName).push(channelName);
2056
+ }
2057
+ }
2058
+ }
2059
+ if (channel.publish && channel.publish()) {
2060
+ const message = channel.publish().message();
2061
+ if (message) {
2062
+ let messageName = null;
2063
+ if (message.$ref) {
2064
+ messageName = message.$ref.split('/').pop();
2065
+ } else if (message.name) {
2066
+ messageName = typeof message.name === 'function' ? message.name() : message.name;
2067
+ }
2068
+ if (messageName) {
2069
+ if (!messageToChannels.has(messageName)) {
2070
+ messageToChannels.set(messageName, []);
2071
+ }
2072
+ messageToChannels.get(messageName).push(channelName);
2073
+ }
2074
+ }
2075
+ }
2076
+ } catch (e) {
2077
+ // Ignore channel processing errors
2078
+ console.warn(`Error processing channel: ${e.message}`);
2079
+ }
2080
+ }
2081
+ }
2082
+ }
2083
+
2084
+ // Build schema registry from components.schemas
2085
+ // Try to access the raw AsyncAPI document
2086
+ let rawDoc = null;
2087
+ try {
2088
+ if (asyncapi.json && typeof asyncapi.json === 'function') {
2089
+ rawDoc = asyncapi.json();
2090
+ } else if (asyncapi._json) {
2091
+ rawDoc = asyncapi._json;
2092
+ }
2093
+ } catch (e) {
2094
+ // Ignore
2095
+ }
2096
+
2097
+ // Extract schemas from raw document if available
2098
+ if (rawDoc && rawDoc.components && rawDoc.components.schemas) {
2099
+ Object.entries(rawDoc.components.schemas).forEach(([name, schema]) => {
2100
+ if (name && typeof name === 'string' && schema && typeof schema === 'object') {
2101
+ schemaRegistry.set(name, schema);
2102
+ componentSchemas.push({
2103
+ name,
2104
+ rustName: toRustTypeName(name),
2105
+ schema: schema,
2106
+ description: schema.description
2107
+ });
2108
+ }
2109
+ });
2110
+ }
2111
+
2112
+ // Fallback: try the components.schemas() method
2113
+ if (componentSchemas.length === 0 && components && components.schemas) {
2114
+ try {
2115
+ const schemas = components.schemas();
2116
+ if (schemas) {
2117
+ // Try different ways to access schemas
2118
+ let schemaEntries = [];
2119
+ if (schemas instanceof Map) {
2120
+ schemaEntries = Array.from(schemas.entries());
2121
+ } else if (typeof schemas === 'object') {
2122
+ schemaEntries = Object.entries(schemas);
2123
+ } else if (schemas.all && typeof schemas.all === 'function') {
2124
+ // AsyncAPI parser might have an all() method
2125
+ const allSchemas = schemas.all();
2126
+ if (Array.isArray(allSchemas)) {
2127
+ schemaEntries = allSchemas.map(schema => {
2128
+ const name = schema.uid ? schema.uid() : schema.id ? schema.id() : null;
2129
+ return [name, schema];
2130
+ }).filter(([name]) => name);
2131
+ }
2132
+ }
2133
+ schemaEntries.forEach(([name, schema]) => {
2134
+ // Skip internal AsyncAPI parser objects and numeric keys
2135
+ if (!name || name === 'collections' || name === '_meta' || name.startsWith('_') || /^\d+$/.test(name)) {
2136
+ return;
2137
+ }
2138
+ let schemaData = null;
2139
+ let description = null;
2140
+ try {
2141
+ // Handle different schema object types
2142
+ if (schema && typeof schema.json === 'function') {
2143
+ schemaData = schema.json();
2144
+ } else if (schema && typeof schema === 'object') {
2145
+ schemaData = schema;
2146
+ }
2147
+ if (schema && typeof schema.description === 'function') {
2148
+ description = schema.description();
2149
+ } else if (schema && schema.description) {
2150
+ description = schema.description;
2151
+ }
2152
+ } catch (e) {
2153
+ // Ignore schema extraction errors
2154
+ console.warn(`Failed to extract schema for ${name}:`, e.message);
2155
+ }
2156
+ if (schemaData && typeof name === 'string' && name.length > 0) {
2157
+ schemaRegistry.set(name, schemaData);
2158
+ componentSchemas.push({
2159
+ name,
2160
+ rustName: toRustTypeName(name),
2161
+ schema: schemaData,
2162
+ description
2163
+ });
2164
+ }
2165
+ });
2166
+ }
2167
+ } catch (e) {
2168
+ console.warn('Failed to extract component schemas:', e.message);
2169
+ }
2170
+ }
2171
+
2172
+ // Extract messages from components
2173
+ if (components && components.messages) {
2174
+ const messages = components.messages();
2175
+ if (messages) {
2176
+ Object.entries(messages).forEach(([name, message]) => {
2177
+ let payload = null;
2178
+ let description = null;
2179
+ let title = null;
2180
+ let messageName = name;
2181
+ try {
2182
+ let rawPayload = null;
2183
+ if (message.payload && typeof message.payload === 'function') {
2184
+ const payloadSchema = message.payload();
2185
+ payload = payloadSchema && payloadSchema.json ? payloadSchema.json() : payloadSchema;
2186
+ // Try to get the raw payload reference from the message
2187
+ if (message._json && message._json.payload) {
2188
+ rawPayload = message._json.payload;
2189
+ }
2190
+ }
2191
+ description = message.description && typeof message.description === 'function' ? message.description() : null;
2192
+ title = message.title && typeof message.title === 'function' ? message.title() : null;
2193
+
2194
+ // Try to get the actual message name
2195
+ if (message.name && typeof message.name === 'function') {
2196
+ messageName = message.name();
2197
+ } else if (message.name) {
2198
+ messageName = message.name;
2199
+ }
2200
+
2201
+ // If we have raw document access, try to get the payload reference from there
2202
+ if (!rawPayload && rawDoc && rawDoc.components && rawDoc.components.messages && rawDoc.components.messages[name]) {
2203
+ rawPayload = rawDoc.components.messages[name].payload;
2204
+ }
2205
+ const channels = messageToChannels.get(messageName) || messageToChannels.get(name) || [];
2206
+ messageSchemas.push({
2207
+ name: messageName,
2208
+ rustName: toRustTypeName(messageName),
2209
+ payload,
2210
+ rawPayload,
2211
+ description: description || title,
2212
+ channels
2213
+ });
2214
+ } catch (e) {
2215
+ // Ignore payload extraction errors
2216
+ const channels = messageToChannels.get(messageName) || messageToChannels.get(name) || [];
2217
+ messageSchemas.push({
2218
+ name: messageName,
2219
+ rustName: toRustTypeName(messageName),
2220
+ payload,
2221
+ rawPayload: null,
2222
+ description: description || title,
2223
+ channels
2224
+ });
2225
+ }
2226
+ });
2227
+ }
2228
+ }
2229
+
2230
+ // Helper function to convert JSON schema to Rust type
2231
+ function jsonSchemaToRustType(schema, typeName = null) {
2232
+ if (!schema) return 'serde_json::Value';
2233
+
2234
+ // Handle $ref
2235
+ if (schema.$ref) {
2236
+ const refName = schema.$ref.split('/').pop();
2237
+ const rustTypeName = toRustTypeName(refName);
2238
+
2239
+ // Generate the referenced schema if we have access to components
2240
+ if (components && components.schemas) {
2241
+ const schemas = components.schemas();
2242
+ if (schemas && schemas[refName] && !generatedTypes.has(rustTypeName)) {
2243
+ generatedTypes.add(rustTypeName);
2244
+ const referencedSchema = schemas[refName];
2245
+ const schemaJson = referencedSchema.json ? referencedSchema.json() : referencedSchema;
2246
+ nestedSchemas.set(rustTypeName, {
2247
+ type: 'struct',
2248
+ schema: schemaJson,
2249
+ description: referencedSchema.description && typeof referencedSchema.description === 'function' ? referencedSchema.description() : null
2250
+ });
2251
+ }
2252
+ }
2253
+ return rustTypeName;
2254
+ }
2255
+
2256
+ // Handle resolved $ref - check for x-parser-schema-id which indicates original schema name
2257
+ if (schema['x-parser-schema-id'] && typeof schema['x-parser-schema-id'] === 'string') {
2258
+ const schemaId = schema['x-parser-schema-id'];
2259
+ // Check if this matches a known component schema
2260
+ if (schemaRegistry.has(schemaId)) {
2261
+ const rustTypeName = toRustTypeName(schemaId);
2262
+ return rustTypeName;
2263
+ }
2264
+ }
2265
+ if (!schema.type) {
2266
+ // If no type specified, check for properties (object) or items (array)
2267
+ if (schema.properties) {
2268
+ schema.type = 'object';
2269
+ } else if (schema.items) {
2270
+ schema.type = 'array';
2271
+ } else {
2272
+ return 'serde_json::Value';
2273
+ }
2274
+ }
2275
+ switch (schema.type) {
2276
+ case 'string':
2277
+ if (schema.enum && schema.enum.length > 0) {
2278
+ // Generate enum type
2279
+ if (typeName) {
2280
+ const enumName = `${typeName}Enum`;
2281
+ if (!generatedTypes.has(enumName)) {
2282
+ generatedTypes.add(enumName);
2283
+ nestedSchemas.set(enumName, {
2284
+ type: 'enum',
2285
+ variants: schema.enum,
2286
+ description: schema.description
2287
+ });
2288
+ }
2289
+ return enumName;
2290
+ }
2291
+ return 'String'; // Fallback if no type name provided
2292
+ }
2293
+ if (schema.format === 'date-time') return 'chrono::DateTime<chrono::Utc>';
2294
+ if (schema.format === 'uuid') return 'uuid::Uuid';
2295
+ if (schema.format === 'email') return 'String';
2296
+ if (schema.format === 'uri') return 'String';
2297
+ return 'String';
2298
+ case 'integer':
2299
+ switch (schema.format) {
2300
+ case 'int32':
2301
+ return 'i32';
2302
+ case 'int64':
2303
+ return 'i64';
2304
+ case 'uint32':
2305
+ return 'u32';
2306
+ case 'uint64':
2307
+ return 'u64';
2308
+ case 'uint16':
2309
+ return 'u16';
2310
+ case 'uint8':
2311
+ return 'u8';
2312
+ case 'int16':
2313
+ return 'i16';
2314
+ case 'int8':
2315
+ return 'i8';
2316
+ default:
2317
+ // Default to i32 for unspecified format (maintains backward compatibility)
2318
+ return 'i32';
2319
+ }
2320
+ case 'number':
2321
+ return 'f64';
2322
+ case 'boolean':
2323
+ return 'bool';
2324
+ case 'array':
2325
+ {
2326
+ const itemType = jsonSchemaToRustType(schema.items);
2327
+ return `Vec<${itemType}>`;
2328
+ }
2329
+ case 'object':
2330
+ if (schema.properties && Object.keys(schema.properties).length > 0) {
2331
+ // Generate nested struct
2332
+ if (typeName) {
2333
+ const structName = toRustTypeName(typeName);
2334
+ if (!generatedTypes.has(structName)) {
2335
+ generatedTypes.add(structName);
2336
+ nestedSchemas.set(structName, {
2337
+ type: 'struct',
2338
+ schema: schema,
2339
+ description: schema.description
2340
+ });
2341
+ }
2342
+ return structName;
2343
+ }
2344
+ }
2345
+ return 'serde_json::Value';
2346
+ default:
2347
+ return 'serde_json::Value';
2348
+ }
2349
+ }
2350
+
2351
+ // Generate message structs
2352
+ function generateMessageStruct(schema, messageName) {
2353
+ if (!schema || !schema.properties) {
2354
+ return ' pub data: serde_json::Value,';
2355
+ }
2356
+ const fields = Object.entries(schema.properties).map(([fieldName, fieldSchema]) => {
2357
+ const rustFieldName = toRustFieldName(fieldName);
2358
+ const fieldTypeName = `${messageName}${toRustTypeName(fieldName)}`;
2359
+ const rustType = jsonSchemaToRustType(fieldSchema, fieldTypeName);
2360
+ const requiredFields = schema.required;
2361
+ const optional = !requiredFields || !Array.isArray(requiredFields) || requiredFields.indexOf(fieldName) === -1;
2362
+ const finalType = optional ? `Option<${rustType}>` : rustType;
2363
+ let fieldDoc = '';
2364
+ if (fieldSchema.description) {
2365
+ fieldDoc = ` /// ${fieldSchema.description}\n`;
2366
+ }
2367
+ let serdeRename = '';
2368
+ if (rustFieldName !== fieldName) {
2369
+ serdeRename = ` #[serde(rename = "${fieldName}")]\n`;
2370
+ }
2371
+ let skipSerializing = '';
2372
+ if (optional) {
2373
+ skipSerializing = ' #[serde(skip_serializing_if = "Option::is_none")]\n';
2374
+ }
2375
+ return `${fieldDoc}${serdeRename}${skipSerializing} pub ${rustFieldName}: ${finalType},`;
2376
+ }).join('\n');
2377
+ return fields;
2378
+ }
2379
+
2380
+ // Process all component schemas first to ensure they are available for references
2381
+ componentSchemas.forEach(schema => {
2382
+ jsonSchemaToRustType(schema.schema, schema.rustName);
2383
+ generatedTypes.add(schema.rustName);
2384
+ });
2385
+
2386
+ // Process all message schemas to ensure all referenced types are generated
2387
+ messageSchemas.forEach(schema => {
2388
+ if (schema.payload) {
2389
+ jsonSchemaToRustType(schema.payload, schema.rustName);
2390
+ }
2391
+ });
2392
+
2393
+ // Return the processed data and generation functions
2394
+ return {
2395
+ messageSchemas,
2396
+ componentSchemas,
2397
+ messageToChannels,
2398
+ generatedTypes,
2399
+ nestedSchemas,
2400
+ schemaRegistry,
2401
+ // Generation functions
2402
+ generateMessageStruct,
2403
+ jsonSchemaToRustType,
2404
+ // Generate component schema definitions
2405
+ generateComponentSchemas() {
2406
+ let result = '';
2407
+ componentSchemas.forEach(schema => {
2408
+ const doc = schema.description ? `/// ${schema.description}\n` : `/// ${schema.name}\n`;
2409
+
2410
+ // Check if this is a standalone enum schema
2411
+ if (schema.schema.type === 'string' && schema.schema.enum && Array.isArray(schema.schema.enum)) {
2412
+ // Generate enum definition with serde rename attributes for lowercase serialization
2413
+ const variants = schema.schema.enum.map(variant => {
2414
+ const {
2415
+ rustName,
2416
+ serializedName
2417
+ } = toRustEnumVariantWithSerde(variant);
2418
+ return ` #[serde(rename = "${serializedName}")]\n ${rustName}`;
2419
+ }).join(',\n');
2420
+ result += `
2421
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2422
+ pub enum ${schema.rustName} {
2423
+ ${variants},
2424
+ }
2425
+ `;
2426
+ } else if (schema.schema.type && !schema.schema.properties) {
2427
+ // Handle primitive types and arrays without properties
2428
+ // Don't use jsonSchemaToRustType here as it might return the type name itself
2429
+ let primitiveType = 'serde_json::Value';
2430
+ switch (schema.schema.type) {
2431
+ case 'string':
2432
+ if (schema.schema.format === 'date-time') primitiveType = 'chrono::DateTime<chrono::Utc>';else if (schema.schema.format === 'uuid') primitiveType = 'uuid::Uuid';else primitiveType = 'String';
2433
+ break;
2434
+ case 'integer':
2435
+ switch (schema.schema.format) {
2436
+ case 'int32':
2437
+ primitiveType = 'i32';
2438
+ break;
2439
+ case 'int64':
2440
+ primitiveType = 'i64';
2441
+ break;
2442
+ case 'uint32':
2443
+ primitiveType = 'u32';
2444
+ break;
2445
+ case 'uint64':
2446
+ primitiveType = 'u64';
2447
+ break;
2448
+ default:
2449
+ primitiveType = 'i32';
2450
+ break;
2451
+ }
2452
+ break;
2453
+ case 'number':
2454
+ primitiveType = 'f64';
2455
+ break;
2456
+ case 'boolean':
2457
+ primitiveType = 'bool';
2458
+ break;
2459
+ case 'array':
2460
+ if (schema.schema.items) {
2461
+ // Handle array items
2462
+ let itemType = 'serde_json::Value';
2463
+ if (schema.schema.items.type === 'integer') {
2464
+ switch (schema.schema.items.format) {
2465
+ case 'int32':
2466
+ itemType = 'i32';
2467
+ break;
2468
+ case 'int64':
2469
+ itemType = 'i64';
2470
+ break;
2471
+ case 'uint32':
2472
+ itemType = 'u32';
2473
+ break;
2474
+ case 'uint64':
2475
+ itemType = 'u64';
2476
+ break;
2477
+ case 'uint8':
2478
+ itemType = 'u8';
2479
+ break;
2480
+ case 'int8':
2481
+ itemType = 'i8';
2482
+ break;
2483
+ case 'uint16':
2484
+ itemType = 'u16';
2485
+ break;
2486
+ case 'int16':
2487
+ itemType = 'i16';
2488
+ break;
2489
+ default:
2490
+ itemType = 'i32';
2491
+ break;
2492
+ }
2493
+ } else if (schema.schema.items.type === 'string') {
2494
+ if (schema.schema.items.format === 'date-time') itemType = 'chrono::DateTime<chrono::Utc>';else if (schema.schema.items.format === 'uuid') itemType = 'uuid::Uuid';else itemType = 'String';
2495
+ } else if (schema.schema.items.type === 'number') {
2496
+ itemType = 'f64';
2497
+ } else if (schema.schema.items.type === 'boolean') {
2498
+ itemType = 'bool';
2499
+ }
2500
+ primitiveType = `Vec<${itemType}>`;
2501
+ } else {
2502
+ primitiveType = 'Vec<serde_json::Value>';
2503
+ }
2504
+ break;
2505
+ }
2506
+
2507
+ // Generate a newtype wrapper for primitive types and arrays
2508
+ result += `
2509
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize)]
2510
+ pub struct ${schema.rustName}(pub ${primitiveType});
2511
+
2512
+ impl From<${primitiveType}> for ${schema.rustName} {
2513
+ fn from(value: ${primitiveType}) -> Self {
2514
+ Self(value)
2515
+ }
2516
+ }
2517
+
2518
+ impl From<${schema.rustName}> for ${primitiveType} {
2519
+ fn from(value: ${schema.rustName}) -> Self {
2520
+ value.0
2521
+ }
2522
+ }
2523
+
2524
+ impl std::fmt::Display for ${schema.rustName} {
2525
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2526
+ ${(() => {
2527
+ // Generate appropriate Display implementation based on the primitive type
2528
+ if (primitiveType.startsWith('Vec<u8>')) {
2529
+ return `self.0.iter().try_for_each(|byte| write!(f, "{:02x}", byte))`;
2530
+ } else if (primitiveType.startsWith('Vec<')) {
2531
+ return 'write!(f, "{:?}", self.0)';
2532
+ } else {
2533
+ return 'write!(f, "{}", self.0)';
2534
+ }
2535
+ })()}
2536
+ }
2537
+ }
2538
+ `;
2539
+ } else {
2540
+ // Generate struct definition
2541
+ const fields = generateMessageStruct(schema.schema, schema.rustName);
2542
+ result += `
2543
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize)]
2544
+ pub struct ${schema.rustName} {
2545
+ ${fields}
2546
+ }
2547
+ `;
2548
+ }
2549
+ });
2550
+ return result;
2551
+ },
2552
+ // Generate nested type definitions
2553
+ generateNestedTypes() {
2554
+ let result = '';
2555
+ for (const [typeName, typeInfo] of nestedSchemas.entries()) {
2556
+ // Don't skip enums - they need to be generated even if the parent type exists
2557
+ const isEnum = typeInfo.type === 'enum';
2558
+ const isComponentSchema = componentSchemas.some(cs => cs.rustName === typeName);
2559
+
2560
+ // Skip if this type was already generated as a component schema (but not enums)
2561
+ if (!isEnum && isComponentSchema) {
2562
+ continue;
2563
+ }
2564
+ if (typeInfo.type === 'enum') {
2565
+ const variants = typeInfo.variants.map(variant => {
2566
+ const {
2567
+ rustName,
2568
+ serializedName
2569
+ } = toRustEnumVariantWithSerde(variant);
2570
+ return ` #[serde(rename = "${serializedName}")]\n ${rustName}`;
2571
+ }).join(',\n');
2572
+ const doc = typeInfo.description ? `/// ${typeInfo.description}\n` : '';
2573
+ result += `
2574
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2575
+ pub enum ${typeName} {
2576
+ ${variants},
2577
+ }
2578
+ `;
2579
+ } else if (typeInfo.type === 'struct') {
2580
+ const fields = generateMessageStruct(typeInfo.schema, typeName);
2581
+ const doc = typeInfo.description ? `/// ${typeInfo.description}\n` : '';
2582
+ result += `
2583
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize)]
2584
+ pub struct ${typeName} {
2585
+ ${fields}
2586
+ }
2587
+ `;
2588
+ }
2589
+ }
2590
+ return result;
2591
+ },
2592
+ // Generate AsyncApiMessage trait implementations (optional)
2593
+ generateAsyncApiTrait() {
2594
+ if (!includeAsyncApiTrait) {
2595
+ return '';
2596
+ }
2597
+
2598
+ // Track which types have already had AsyncApiMessage implementations generated
2599
+ const implementedTypes = new Set();
2600
+ const implementations = [];
2601
+
2602
+ // First add the trait definition
2603
+ implementations.push(`
2604
+ /// Base trait for all AsyncAPI messages providing runtime type information
2605
+ ///
2606
+ /// This trait enables:
2607
+ /// - **Dynamic message routing**: Route messages based on their type at runtime
2608
+ /// - **Channel identification**: Determine which channel a message belongs to
2609
+ /// - **Logging and monitoring**: Track message types for observability
2610
+ /// - **Protocol abstraction**: Handle different message types uniformly
2611
+ pub trait AsyncApiMessage {
2612
+ /// Returns the message type identifier as defined in the AsyncAPI specification
2613
+ ///
2614
+ /// This is used for:
2615
+ /// - Message routing and dispatch
2616
+ /// - Logging and monitoring
2617
+ /// - Protocol-level message identification
2618
+ fn message_type(&self) -> &'static str;
2619
+
2620
+ /// Returns the primary channel this message is associated with
2621
+ ///
2622
+ /// Used for:
2623
+ /// - Default routing when channel is not explicitly specified
2624
+ /// - Message categorization and organization
2625
+ /// - Channel-based access control and filtering
2626
+ fn channel(&self) -> &'static str;
2627
+ }`);
2628
+ messageSchemas.forEach(schema => {
2629
+ const doc = schema.description ? `/// ${schema.description}` : `/// ${schema.name} message`;
2630
+ const primaryChannel = schema.channels.length > 0 ? schema.channels[0] : 'default';
2631
+
2632
+ // Check if the message payload references a component schema
2633
+ let payloadRustName = null;
2634
+ let isComponentMessage = false;
2635
+ if (schema.rawPayload && schema.rawPayload.$ref) {
2636
+ const refName = schema.rawPayload.$ref.split('/').pop();
2637
+ payloadRustName = toRustTypeName(refName);
2638
+ isComponentMessage = true;
2639
+ } else if (schema.payload && schema.payload.$ref) {
2640
+ const refName = schema.payload.$ref.split('/').pop();
2641
+ payloadRustName = toRustTypeName(refName);
2642
+ isComponentMessage = true;
2643
+ } else if (schema.payload && schema.payload['x-parser-schema-id']) {
2644
+ // Handle resolved $ref references
2645
+ const schemaId = schema.payload['x-parser-schema-id'];
2646
+ if (schemaRegistry.has(schemaId)) {
2647
+ payloadRustName = toRustTypeName(schemaId);
2648
+ isComponentMessage = true;
2649
+ }
2650
+ }
2651
+
2652
+ // For component messages, always generate the message wrapper type
2653
+ // even if the payload schema already exists
2654
+ if (isComponentMessage && payloadRustName && !implementedTypes.has(schema.rustName)) {
2655
+ implementedTypes.add(schema.rustName);
2656
+ implementations.push(`
2657
+ ${doc}
2658
+ #[derive(Debug, Clone, Serialize, Deserialize)]
2659
+ pub struct ${schema.rustName} {
2660
+ #[serde(flatten)]
2661
+ pub payload: ${payloadRustName},
2662
+ }
2663
+
2664
+ impl AsyncApiMessage for ${schema.rustName} {
2665
+ fn message_type(&self) -> &'static str {
2666
+ "${schema.name}"
2667
+ }
2668
+
2669
+ fn channel(&self) -> &'static str {
2670
+ "${primaryChannel}"
2671
+ }
2672
+ }`);
2673
+ } else if (!generatedTypes.has(schema.rustName) && !implementedTypes.has(schema.rustName)) {
2674
+ // Generate both struct and implementation for inline message schemas
2675
+ implementedTypes.add(schema.rustName);
2676
+ implementations.push(`
2677
+ ${doc}
2678
+ #[derive(Debug, Clone, Serialize, Deserialize)]
2679
+ pub struct ${schema.rustName} {
2680
+ ${generateMessageStruct(schema.payload, schema.rustName)}
2681
+ }
2682
+
2683
+ impl AsyncApiMessage for ${schema.rustName} {
2684
+ fn message_type(&self) -> &'static str {
2685
+ "${schema.name}"
2686
+ }
2687
+
2688
+ fn channel(&self) -> &'static str {
2689
+ "${primaryChannel}"
2690
+ }
2691
+ }`);
2692
+ } else if (payloadRustName && generatedTypes.has(payloadRustName) && !implementedTypes.has(payloadRustName)) {
2693
+ // Generate AsyncApiMessage implementation for existing component schema
2694
+ implementedTypes.add(payloadRustName);
2695
+ implementations.push(`
2696
+ impl AsyncApiMessage for ${payloadRustName} {
2697
+ fn message_type(&self) -> &'static str {
2698
+ "${schema.name}"
2699
+ }
2700
+
2701
+ fn channel(&self) -> &'static str {
2702
+ "${primaryChannel}"
2703
+ }
2704
+ }`);
2705
+ }
2706
+ });
2707
+ return implementations.join('');
2708
+ }
2709
+ };
2710
+ }
2711
+ ;// ../common/src/envelope-rust.js
2712
+ /* eslint-disable no-unused-vars */
2713
+
2714
+
2715
+ /**
2716
+ * Generate a unified MessageEnvelope for both rust-server and rust-client templates
2717
+ * This envelope includes all features needed by both templates:
2718
+ * - Basic message structure (id, operation, payload, timestamp)
2719
+ * - Request/response patterns (correlation_id, create_response)
2720
+ * - Channel routing (channel field)
2721
+ * - Error handling (error field, error methods)
2722
+ * - Authentication (auth header methods)
2723
+ * - Serialization (to_bytes, from_bytes)
2724
+ */
2725
+ function generateMessageEnvelope() {
2726
+ return `use serde::{de::DeserializeOwned, Deserialize, Serialize};
2727
+ use std::collections::HashMap;
2728
+ use uuid::Uuid;
2729
+ use chrono::{DateTime, Utc};
2730
+
2731
+ /// Correlation ID for tracing errors across operations
2732
+ #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
2733
+ pub struct CorrelationId(pub Uuid);
2734
+
2735
+ impl CorrelationId {
2736
+ pub fn new() -> Self {
2737
+ Self(Uuid::new_v4())
2738
+ }
2739
+ }
2740
+
2741
+ impl std::fmt::Display for CorrelationId {
2742
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2743
+ write!(f, "{}", self.0)
2744
+ }
2745
+ }
2746
+
2747
+ impl Default for CorrelationId {
2748
+ fn default() -> Self {
2749
+ Self::new()
2750
+ }
2751
+ }
2752
+
2753
+ /// Error severity levels for categorization and alerting
2754
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2755
+ pub enum ErrorSeverity {
2756
+ /// Low severity - informational, no action required
2757
+ Low,
2758
+ /// Medium severity - warning, monitoring required
2759
+ Medium,
2760
+ /// High severity - error, immediate attention needed
2761
+ High,
2762
+ /// Critical severity - system failure, urgent action required
2763
+ Critical,
2764
+ }
2765
+
2766
+ impl std::fmt::Display for ErrorSeverity {
2767
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2768
+ match self {
2769
+ ErrorSeverity::Low => write!(f, "LOW"),
2770
+ ErrorSeverity::Medium => write!(f, "MEDIUM"),
2771
+ ErrorSeverity::High => write!(f, "HIGH"),
2772
+ ErrorSeverity::Critical => write!(f, "CRITICAL"),
2773
+ }
2774
+ }
2775
+ }
2776
+
2777
+ /// Error category for classification and handling
2778
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2779
+ pub enum ErrorCategory {
2780
+ /// Configuration-related errors
2781
+ Configuration,
2782
+ /// Network and protocol errors
2783
+ Network,
2784
+ /// Message validation errors
2785
+ Validation,
2786
+ /// Business logic errors
2787
+ BusinessLogic,
2788
+ /// System resource errors
2789
+ Resource,
2790
+ /// Security-related errors
2791
+ Security,
2792
+ /// Serialization/deserialization errors
2793
+ Serialization,
2794
+ /// Routing errors
2795
+ Routing,
2796
+ /// Authorization errors
2797
+ Authorization,
2798
+ /// Unknown or unclassified errors
2799
+ Unknown,
2800
+ }
2801
+
2802
+ impl std::fmt::Display for ErrorCategory {
2803
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2804
+ match self {
2805
+ ErrorCategory::Configuration => write!(f, "CONFIGURATION"),
2806
+ ErrorCategory::Network => write!(f, "NETWORK"),
2807
+ ErrorCategory::Validation => write!(f, "VALIDATION"),
2808
+ ErrorCategory::BusinessLogic => write!(f, "BUSINESS_LOGIC"),
2809
+ ErrorCategory::Resource => write!(f, "RESOURCE"),
2810
+ ErrorCategory::Security => write!(f, "SECURITY"),
2811
+ ErrorCategory::Serialization => write!(f, "SERIALIZATION"),
2812
+ ErrorCategory::Routing => write!(f, "ROUTING"),
2813
+ ErrorCategory::Authorization => write!(f, "AUTHORIZATION"),
2814
+ ErrorCategory::Unknown => write!(f, "UNKNOWN"),
2815
+ }
2816
+ }
2817
+ }
2818
+
2819
+ /// Error metadata for enhanced context and monitoring
2820
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2821
+ pub struct ErrorMetadata {
2822
+ pub correlation_id: CorrelationId,
2823
+ pub severity: ErrorSeverity,
2824
+ pub category: ErrorCategory,
2825
+ pub timestamp: DateTime<Utc>,
2826
+ pub retryable: bool,
2827
+ pub kind: u32,
2828
+ #[serde(skip_serializing_if = "Option::is_none")]
2829
+ pub source_location: Option<String>,
2830
+ #[serde(skip_serializing_if = "HashMap::is_empty", default)]
2831
+ pub additional_context: HashMap<String, String>,
2832
+ }
2833
+
2834
+ impl ErrorMetadata {
2835
+ pub fn new(severity: ErrorSeverity, category: ErrorCategory, retryable: bool) -> Self {
2836
+ Self {
2837
+ correlation_id: CorrelationId::new(),
2838
+ severity,
2839
+ category,
2840
+ timestamp: Utc::now(),
2841
+ retryable,
2842
+ kind: 0,
2843
+ source_location: None,
2844
+ additional_context: HashMap::new(),
2845
+ }
2846
+ }
2847
+
2848
+ pub fn with_kind(mut self, kind: u32) -> Self {
2849
+ self.kind = kind;
2850
+ self
2851
+ }
2852
+ }
2853
+
2854
+ /// Serializable AsyncAPI error for wire transmission
2855
+ ///
2856
+ /// This error type can be sent between server and client while preserving
2857
+ /// all the rich error information needed for proper error handling.
2858
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2859
+ #[serde(tag = "error_type", content = "details")]
2860
+ pub enum AsyncApiError {
2861
+ #[serde(rename = "configuration")]
2862
+ Configuration {
2863
+ message: String,
2864
+ metadata: ErrorMetadata,
2865
+ },
2866
+
2867
+ #[serde(rename = "protocol")]
2868
+ Protocol {
2869
+ message: String,
2870
+ protocol: String,
2871
+ metadata: ErrorMetadata,
2872
+ },
2873
+
2874
+ #[serde(rename = "validation")]
2875
+ Validation {
2876
+ message: String,
2877
+ #[serde(skip_serializing_if = "Option::is_none")]
2878
+ field: Option<String>,
2879
+ metadata: ErrorMetadata,
2880
+ },
2881
+
2882
+ #[serde(rename = "handler")]
2883
+ Handler {
2884
+ message: String,
2885
+ handler_name: String,
2886
+ metadata: ErrorMetadata,
2887
+ },
2888
+
2889
+ #[serde(rename = "middleware")]
2890
+ Middleware {
2891
+ message: String,
2892
+ middleware_name: String,
2893
+ metadata: ErrorMetadata,
2894
+ },
2895
+
2896
+ #[serde(rename = "recovery")]
2897
+ Recovery {
2898
+ message: String,
2899
+ attempts: u32,
2900
+ metadata: ErrorMetadata,
2901
+ },
2902
+
2903
+ #[serde(rename = "resource")]
2904
+ Resource {
2905
+ message: String,
2906
+ resource_type: String,
2907
+ metadata: ErrorMetadata,
2908
+ },
2909
+
2910
+ #[serde(rename = "security")]
2911
+ Security {
2912
+ message: String,
2913
+ metadata: ErrorMetadata,
2914
+ },
2915
+
2916
+ #[serde(rename = "authentication")]
2917
+ Authentication {
2918
+ message: String,
2919
+ auth_method: String,
2920
+ metadata: ErrorMetadata,
2921
+ },
2922
+
2923
+ #[serde(rename = "authorization")]
2924
+ Authorization {
2925
+ message: String,
2926
+ required_permissions: Vec<String>,
2927
+ metadata: ErrorMetadata,
2928
+ },
2929
+
2930
+ #[serde(rename = "rate_limit")]
2931
+ RateLimit {
2932
+ message: String,
2933
+ #[serde(skip_serializing_if = "Option::is_none")]
2934
+ retry_after_secs: Option<u64>,
2935
+ },
2936
+
2937
+ #[serde(rename = "context")]
2938
+ Context {
2939
+ message: String,
2940
+ context_key: String,
2941
+ metadata: ErrorMetadata,
2942
+ },
2943
+ }
2944
+
2945
+ impl AsyncApiError {
2946
+ /// Get error message
2947
+ pub fn message(&self) -> &str {
2948
+ match self {
2949
+ AsyncApiError::Configuration { message, .. } => message,
2950
+ AsyncApiError::Protocol { message, .. } => message,
2951
+ AsyncApiError::Validation { message, .. } => message,
2952
+ AsyncApiError::Handler { message, .. } => message,
2953
+ AsyncApiError::Middleware { message, .. } => message,
2954
+ AsyncApiError::Recovery { message, .. } => message,
2955
+ AsyncApiError::Resource { message, .. } => message,
2956
+ AsyncApiError::Security { message, .. } => message,
2957
+ AsyncApiError::Authentication { message, .. } => message,
2958
+ AsyncApiError::Authorization { message, .. } => message,
2959
+ AsyncApiError::RateLimit { message, .. } => message,
2960
+ AsyncApiError::Context { message, .. } => message,
2961
+ }
2962
+ }
2963
+
2964
+ /// Get error metadata (if available)
2965
+ pub fn metadata(&self) -> Option<&ErrorMetadata> {
2966
+ match self {
2967
+ AsyncApiError::Configuration { metadata, .. } => Some(metadata),
2968
+ AsyncApiError::Protocol { metadata, .. } => Some(metadata),
2969
+ AsyncApiError::Validation { metadata, .. } => Some(metadata),
2970
+ AsyncApiError::Handler { metadata, .. } => Some(metadata),
2971
+ AsyncApiError::Middleware { metadata, .. } => Some(metadata),
2972
+ AsyncApiError::Recovery { metadata, .. } => Some(metadata),
2973
+ AsyncApiError::Resource { metadata, .. } => Some(metadata),
2974
+ AsyncApiError::Security { metadata, .. } => Some(metadata),
2975
+ AsyncApiError::Authentication { metadata, .. } => Some(metadata),
2976
+ AsyncApiError::Authorization { metadata, .. } => Some(metadata),
2977
+ AsyncApiError::Context { metadata, .. } => Some(metadata),
2978
+ AsyncApiError::RateLimit { .. } => None,
2979
+ }
2980
+ }
2981
+
2982
+ /// Check if error is retryable
2983
+ pub fn is_retryable(&self) -> bool {
2984
+ self.metadata().map_or(false, |m| m.retryable)
2985
+ }
2986
+
2987
+ /// Get error severity
2988
+ pub fn severity(&self) -> Option<ErrorSeverity> {
2989
+ self.metadata().map(|m| m.severity)
2990
+ }
2991
+
2992
+ /// Get error category
2993
+ pub fn category(&self) -> Option<ErrorCategory> {
2994
+ self.metadata().map(|m| m.category)
2995
+ }
2996
+
2997
+ /// Get correlation ID for tracing
2998
+ pub fn correlation_id(&self) -> Option<&CorrelationId> {
2999
+ self.metadata().map(|m| &m.correlation_id)
3000
+ }
3001
+ }
3002
+
3003
+ impl std::fmt::Display for AsyncApiError {
3004
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3005
+ write!(f, "{}", self.message())
3006
+ }
3007
+ }
3008
+
3009
+ impl std::error::Error for AsyncApiError {}
3010
+
3011
+ /// Unified message envelope for consistent AsyncAPI message format
3012
+ ///
3013
+ /// This envelope provides a standardized structure for all messages sent through the system,
3014
+ /// enabling better correlation, error handling, authentication, and observability.
3015
+ ///
3016
+ /// ## Features
3017
+ ///
3018
+ /// - **Request/Response Patterns**: Correlation IDs for matching requests with responses
3019
+ /// - **Error Handling**: Built-in error information for failed operations
3020
+ /// - **Authentication**: Integrated auth header support
3021
+ /// - **Channel Routing**: Optional channel context for message routing
3022
+ /// - **Serialization**: Efficient byte conversion for transport layers
3023
+ /// - **Type Safety**: Strongly-typed payload extraction
3024
+ ///
3025
+ /// ## Usage
3026
+ ///
3027
+ /// \`\`\`no-run
3028
+ /// use crate::models::*;
3029
+ /// use uuid::Uuid;
3030
+ /// use std::collections::HashMap;
3031
+ ///
3032
+ /// // Create a basic message envelope
3033
+ /// let envelope = MessageEnvelope::new("sendChatMessage", chat_message)?;
3034
+ ///
3035
+ /// // Create with correlation ID for request/response
3036
+ /// let request = MessageEnvelope::new_with_correlation_id(
3037
+ /// "getUserProfile",
3038
+ /// user_request,
3039
+ /// Uuid::new_v4().to_string()
3040
+ /// )?;
3041
+ ///
3042
+ /// // Create response with same correlation ID
3043
+ /// let response = request.create_response("getUserProfile_response", user_profile)?;
3044
+ ///
3045
+ /// // Create error response
3046
+ /// let error = MessageEnvelope::error_response(
3047
+ /// "getUserProfile_response",
3048
+ /// "USER_NOT_FOUND",
3049
+ /// "User does not exist",
3050
+ /// request.correlation_id().map(|s| s.to_string())
3051
+ /// );
3052
+ ///
3053
+ /// // Add authentication headers
3054
+ /// let mut headers = HashMap::new();
3055
+ /// headers.insert("Authorization".to_string(), "Bearer token123".to_string());
3056
+ /// let auth_envelope = envelope.with_headers(headers);
3057
+ ///
3058
+ /// // Serialize for transport
3059
+ /// let bytes = envelope.to_bytes()?;
3060
+ /// let deserialized = MessageEnvelope::from_bytes(&bytes)?;
3061
+ /// \`\`\`
3062
+
3063
+ /// Standard message envelope for all AsyncAPI messages
3064
+ ///
3065
+ /// This envelope provides a consistent structure for all messages sent through the system,
3066
+ /// enabling better correlation, error handling, and observability.
3067
+ #[derive(Debug, Clone, Serialize, Deserialize)]
3068
+ pub struct MessageEnvelope {
3069
+ /// Unique message identifier
3070
+ pub id: String,
3071
+ /// AsyncAPI operation ID
3072
+ pub operation: String,
3073
+ /// Message payload (any serializable type)
3074
+ pub payload: serde_json::Value,
3075
+ /// ISO 8601 timestamp when message was created
3076
+ pub timestamp: String,
3077
+ /// Correlation ID for request/response patterns
3078
+ pub correlation_id: Option<String>,
3079
+ /// Optional channel context for routing
3080
+ pub channel: Option<String>,
3081
+ /// Transport-level headers (auth, routing, etc.)
3082
+ pub headers: Option<HashMap<String, String>>,
3083
+ /// Rich error information if operation failed
3084
+ /// Contains detailed error metadata including severity, category, and correlation
3085
+ pub error: Option<AsyncApiError>,
3086
+ }
3087
+
3088
+ impl MessageEnvelope {
3089
+ /// Create a new message envelope with the given operation and payload
3090
+ pub fn new<T: Serialize>(operation: &str, payload: T) -> Result<Self, serde_json::Error> {
3091
+ Ok(Self {
3092
+ id: Uuid::new_v4().to_string(),
3093
+ operation: operation.to_string(),
3094
+ payload: serde_json::to_value(payload)?,
3095
+ timestamp: chrono::Utc::now().to_rfc3339(),
3096
+ correlation_id: None,
3097
+ channel: None,
3098
+ headers: None,
3099
+ error: None,
3100
+ })
3101
+ }
3102
+
3103
+ /// Create a new envelope with automatic correlation ID generation
3104
+ pub fn new_with_id<T: Serialize>(
3105
+ operation: &str,
3106
+ payload: T,
3107
+ ) -> Result<Self, serde_json::Error> {
3108
+ Self::new(operation, payload)
3109
+ .map(|envelope| envelope.with_correlation_id(Uuid::new_v4().to_string()))
3110
+ }
3111
+
3112
+ /// Create a new message envelope with a specific correlation ID
3113
+ pub fn new_with_correlation_id<T: Serialize>(
3114
+ operation: &str,
3115
+ payload: T,
3116
+ correlation_id: String,
3117
+ ) -> Result<Self, serde_json::Error> {
3118
+ let mut envelope = Self::new(operation, payload)?;
3119
+ envelope.correlation_id = Some(correlation_id);
3120
+ Ok(envelope)
3121
+ }
3122
+
3123
+ /// Create an error response envelope with rich AsyncApiError
3124
+ pub fn error_response(
3125
+ operation: &str,
3126
+ error: AsyncApiError,
3127
+ correlation_id: Option<String>,
3128
+ ) -> Self {
3129
+ Self {
3130
+ id: Uuid::new_v4().to_string(),
3131
+ operation: operation.to_string(),
3132
+ payload: serde_json::Value::Null,
3133
+ timestamp: chrono::Utc::now().to_rfc3339(),
3134
+ correlation_id,
3135
+ channel: None,
3136
+ headers: None,
3137
+ error: Some(error),
3138
+ }
3139
+ }
3140
+
3141
+ /// Create a simple error response envelope (for backward compatibility)
3142
+ pub fn simple_error_response(
3143
+ operation: &str,
3144
+ error_message: &str,
3145
+ correlation_id: Option<String>,
3146
+ ) -> Self {
3147
+ let error = AsyncApiError::Handler {
3148
+ message: error_message.to_string(),
3149
+ handler_name: operation.to_string(),
3150
+ metadata: ErrorMetadata::new(
3151
+ ErrorSeverity::High,
3152
+ ErrorCategory::BusinessLogic,
3153
+ false,
3154
+ ),
3155
+ };
3156
+ Self::error_response(operation, error, correlation_id)
3157
+ }
3158
+
3159
+ /// Set the correlation ID for this envelope
3160
+ pub fn with_correlation_id(mut self, id: String) -> Self {
3161
+ self.correlation_id = Some(id);
3162
+ self
3163
+ }
3164
+
3165
+ /// Set the channel for this envelope
3166
+ pub fn with_channel(mut self, channel: String) -> Self {
3167
+ self.channel = Some(channel);
3168
+ self
3169
+ }
3170
+
3171
+ /// Set headers for this envelope
3172
+ pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
3173
+ self.headers = Some(headers);
3174
+ self
3175
+ }
3176
+
3177
+ /// Add a single header to this envelope
3178
+ pub fn with_header(mut self, key: String, value: String) -> Self {
3179
+ if self.headers.is_none() {
3180
+ self.headers = Some(HashMap::new());
3181
+ }
3182
+ if let Some(ref mut headers) = self.headers {
3183
+ headers.insert(key, value);
3184
+ }
3185
+ self
3186
+ }
3187
+
3188
+ /// Add authentication headers to the envelope
3189
+ /// This method accepts any headers map, allowing templates to integrate their own auth systems
3190
+ pub fn with_auth_headers(mut self, auth_headers: HashMap<String, String>) -> Self {
3191
+ if !auth_headers.is_empty() {
3192
+ if let Some(ref mut headers) = self.headers {
3193
+ headers.extend(auth_headers);
3194
+ } else {
3195
+ self.headers = Some(auth_headers);
3196
+ }
3197
+ }
3198
+ self
3199
+ }
3200
+
3201
+ /// Set an error on this envelope
3202
+ pub fn with_error(mut self, error: AsyncApiError) -> Self {
3203
+ self.error = Some(error);
3204
+ self
3205
+ }
3206
+
3207
+ /// Set a simple error on this envelope (for backward compatibility)
3208
+ pub fn with_simple_error(mut self, message: &str) -> Self {
3209
+ let error = AsyncApiError::Handler {
3210
+ message: message.to_string(),
3211
+ handler_name: self.operation.clone(),
3212
+ metadata: ErrorMetadata::new(
3213
+ ErrorSeverity::High,
3214
+ ErrorCategory::BusinessLogic,
3215
+ false,
3216
+ ),
3217
+ };
3218
+ self.error = Some(error);
3219
+ self
3220
+ }
3221
+
3222
+ /// Extract the payload as a strongly-typed message
3223
+ pub fn extract_payload<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
3224
+ serde_json::from_value(self.payload.clone())
3225
+ }
3226
+
3227
+ /// Check if this envelope contains an error
3228
+ pub fn is_error(&self) -> bool {
3229
+ self.error.is_some()
3230
+ }
3231
+
3232
+ /// Get the correlation ID if present
3233
+ pub fn correlation_id(&self) -> Option<&str> {
3234
+ self.correlation_id.as_deref()
3235
+ }
3236
+
3237
+ /// Create a response envelope with the same correlation ID
3238
+ pub fn create_response<T: Serialize>(
3239
+ &self,
3240
+ response_operation: &str,
3241
+ payload: T,
3242
+ ) -> Result<Self, serde_json::Error> {
3243
+ let mut response = Self::new(response_operation, payload)?;
3244
+ response.correlation_id = self.correlation_id.clone();
3245
+ response.channel = self.channel.clone();
3246
+ Ok(response)
3247
+ }
3248
+
3249
+ /// Convert the envelope to bytes for transport
3250
+ pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
3251
+ serde_json::to_vec(self)
3252
+ }
3253
+
3254
+ /// Parse envelope from bytes received from transport
3255
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, serde_json::Error> {
3256
+ serde_json::from_slice(bytes)
3257
+ }
3258
+ }
3259
+
3260
+ `;
3261
+ }
3262
+ ;// ../common/src/models-ts.js
3263
+ /* eslint-disable no-unused-vars */
3264
+
3265
+
3266
+ /**
3267
+ * Generate TypeScript models from AsyncAPI specification
3268
+ * This helper extracts the common schema processing logic used by TypeScript templates
3269
+ *
3270
+ * @param {Object} asyncapi - AsyncAPI document
3271
+ * @param {Object} options - Generation options
3272
+ * @param {Function} options.toTypeScriptTypeName - Function to convert names to TypeScript type names
3273
+ * @param {Function} options.toTypeScriptIdentifier - Function to convert names to TypeScript identifiers
3274
+ * @param {boolean} options.includeMessageTypes - Whether to include message type constants
3275
+ * @returns {Object} Generated models data and functions
3276
+ */
3277
+ function generateTypeScriptModels(asyncapi, options = {}) {
3278
+ const {
3279
+ toTypeScriptTypeName,
3280
+ toTypeScriptIdentifier,
3281
+ includeMessageTypes = true
3282
+ } = options;
3283
+
3284
+ // Helper functions for TypeScript identifier generation
3285
+ function defaultToTypeScriptIdentifier(str) {
3286
+ if (!str) return 'unknown';
3287
+ let identifier = str.replace(/[^a-zA-Z0-9_]/g, '_').replace(/^[0-9]/, '_$&').replace(/_+/g, '_').replace(/^_+|_+$/g, '');
3288
+ if (/^[0-9]/.test(identifier)) {
3289
+ identifier = 'item_' + identifier;
3290
+ }
3291
+ if (!identifier) {
3292
+ identifier = 'unknown';
3293
+ }
3294
+ return identifier;
3295
+ }
3296
+ function defaultToTypeScriptTypeName(str) {
3297
+ if (!str) return 'Unknown';
3298
+ // Handle camelCase and PascalCase properly
3299
+ const identifier = str.replace(/[^a-zA-Z0-9]/g, '_').replace(/^[0-9]/, '_$&').replace(/_+/g, '_').replace(/^_+|_+$/g, '');
3300
+
3301
+ // Split on underscores and camelCase boundaries
3302
+ const parts = identifier.split(/[_\s]+|(?=[A-Z])/);
3303
+ return parts.filter(part => part.length > 0).map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
3304
+ }
3305
+
3306
+ // Use provided functions or defaults
3307
+ const toTSTypeName = toTypeScriptTypeName || defaultToTypeScriptTypeName;
3308
+ const toTSIdentifier = toTypeScriptIdentifier || defaultToTypeScriptIdentifier;
3309
+
3310
+ // Extract message schemas and build channel mapping
3311
+ const components = asyncapi.components();
3312
+ const messageSchemas = [];
3313
+ const componentSchemas = [];
3314
+ const messageToChannels = new Map();
3315
+ const generatedTypes = new Set();
3316
+ const schemaRegistry = new Map();
3317
+
3318
+ // Build schema registry from components.schemas
3319
+ // Try to access the raw AsyncAPI document
3320
+ let rawDoc = null;
3321
+ try {
3322
+ if (asyncapi.json && typeof asyncapi.json === 'function') {
3323
+ rawDoc = asyncapi.json();
3324
+ } else if (asyncapi._json) {
3325
+ rawDoc = asyncapi._json;
3326
+ }
3327
+ } catch (e) {
3328
+ // Ignore
3329
+ }
3330
+
3331
+ // Extract schemas from raw document if available
3332
+ if (rawDoc && rawDoc.components && rawDoc.components.schemas) {
3333
+ Object.entries(rawDoc.components.schemas).forEach(([name, schema]) => {
3334
+ if (name && typeof name === 'string' && schema && typeof schema === 'object') {
3335
+ schemaRegistry.set(name, schema);
3336
+ componentSchemas.push({
3337
+ name,
3338
+ typeName: toTSTypeName(name),
3339
+ schema: schema,
3340
+ description: schema.description
3341
+ });
3342
+ }
3343
+ });
3344
+ }
3345
+
3346
+ // Fallback: try the components.schemas() method
3347
+ if (componentSchemas.length === 0 && components && components.schemas) {
3348
+ try {
3349
+ const schemas = components.schemas();
3350
+ if (schemas) {
3351
+ // Try different ways to access schemas
3352
+ let schemaEntries = [];
3353
+ if (schemas instanceof Map) {
3354
+ schemaEntries = Array.from(schemas.entries());
3355
+ } else if (typeof schemas === 'object') {
3356
+ schemaEntries = Object.entries(schemas);
3357
+ } else if (schemas.all && typeof schemas.all === 'function') {
3358
+ // AsyncAPI parser might have an all() method
3359
+ const allSchemas = schemas.all();
3360
+ if (Array.isArray(allSchemas)) {
3361
+ schemaEntries = allSchemas.map(schema => {
3362
+ const name = schema.uid ? schema.uid() : schema.id ? schema.id() : null;
3363
+ return [name, schema];
3364
+ }).filter(([name]) => name);
3365
+ }
3366
+ }
3367
+ schemaEntries.forEach(([name, schema]) => {
3368
+ // Skip internal AsyncAPI parser objects and numeric keys
3369
+ if (!name || name === 'collections' || name === '_meta' || name.startsWith('_') || /^\d+$/.test(name)) {
3370
+ return;
3371
+ }
3372
+ let schemaData = null;
3373
+ let description = null;
3374
+ try {
3375
+ // Handle different schema object types
3376
+ if (schema && typeof schema.json === 'function') {
3377
+ schemaData = schema.json();
3378
+ } else if (schema && typeof schema === 'object') {
3379
+ schemaData = schema;
3380
+ }
3381
+ if (schema && typeof schema.description === 'function') {
3382
+ description = schema.description();
3383
+ } else if (schema && schema.description) {
3384
+ description = schema.description;
3385
+ }
3386
+ } catch (e) {
3387
+ // Ignore schema extraction errors
3388
+ console.warn(`Failed to extract schema for ${name}:`, e.message);
3389
+ }
3390
+ if (schemaData && typeof name === 'string' && name.length > 0) {
3391
+ schemaRegistry.set(name, schemaData);
3392
+ componentSchemas.push({
3393
+ name,
3394
+ typeName: toTSTypeName(name),
3395
+ schema: schemaData,
3396
+ description
3397
+ });
3398
+ }
3399
+ });
3400
+ }
3401
+ } catch (e) {
3402
+ console.warn('Failed to extract component schemas:', e.message);
3403
+ }
3404
+ }
3405
+
3406
+ // First, build channel to message mapping
3407
+ if (asyncapi.channels) {
3408
+ const channels = asyncapi.channels();
3409
+ if (channels) {
3410
+ Object.entries(channels).forEach(([channelName, channel]) => {
3411
+ try {
3412
+ // Handle AsyncAPI 3.x format
3413
+ if (channel.messages) {
3414
+ const messages = channel.messages();
3415
+ if (messages) {
3416
+ Object.entries(messages).forEach(([msgKey, message]) => {
3417
+ if (message) {
3418
+ let messageName = null;
3419
+ if (message.$ref) {
3420
+ messageName = message.$ref.split('/').pop();
3421
+ } else if (message.name) {
3422
+ messageName = typeof message.name === 'function' ? message.name() : message.name;
3423
+ }
3424
+ if (messageName) {
3425
+ if (!messageToChannels.has(messageName)) {
3426
+ messageToChannels.set(messageName, []);
3427
+ }
3428
+ messageToChannels.get(messageName).push(channelName);
3429
+ }
3430
+ }
3431
+ });
3432
+ }
3433
+ }
3434
+ } catch (e) {
3435
+ // Ignore channel processing errors
3436
+ }
3437
+ });
3438
+ }
3439
+ }
3440
+
3441
+ // Extract messages from components
3442
+ if (components && components.messages) {
3443
+ const messages = components.messages();
3444
+ if (messages) {
3445
+ Object.entries(messages).forEach(([name, message]) => {
3446
+ // Skip internal AsyncAPI parser objects
3447
+ if (name === 'collections' || name === '_meta' || name.startsWith('_')) {
3448
+ return;
3449
+ }
3450
+ let payload = null;
3451
+ let description = null;
3452
+ let title = null;
3453
+ let messageName = name;
3454
+ try {
3455
+ if (message.payload && typeof message.payload === 'function') {
3456
+ const payloadSchema = message.payload();
3457
+ payload = payloadSchema && payloadSchema.json ? payloadSchema.json() : payloadSchema;
3458
+ }
3459
+ description = message.description && typeof message.description === 'function' ? message.description() : message.description;
3460
+ title = message.title && typeof message.title === 'function' ? message.title() : message.title;
3461
+
3462
+ // Try to get the actual message name
3463
+ if (message.name && typeof message.name === 'function') {
3464
+ messageName = message.name();
3465
+ } else if (message.name) {
3466
+ messageName = message.name;
3467
+ }
3468
+ } catch (e) {
3469
+ // Ignore payload extraction errors
3470
+ }
3471
+ const channels = messageToChannels.get(messageName) || messageToChannels.get(name) || [];
3472
+ messageSchemas.push({
3473
+ name: messageName,
3474
+ typeName: toTSTypeName(messageName),
3475
+ payload,
3476
+ description: description || title,
3477
+ channels
3478
+ });
3479
+ });
3480
+ }
3481
+ }
3482
+
3483
+ // Helper function to convert JSON schema to TypeScript type
3484
+ function jsonSchemaToTypeScriptType(schema, fieldName = '', isComponentSchemaDefinition = false) {
3485
+ if (!schema) return 'any';
3486
+
3487
+ // Handle $ref - resolve from schema registry
3488
+ if (schema.$ref) {
3489
+ const refName = schema.$ref.split('/').pop();
3490
+ // Always return the type name for $ref, since we generate all component schemas
3491
+ const typeName = toTSTypeName(refName);
3492
+ return typeName;
3493
+ }
3494
+
3495
+ // Handle resolved $ref - check for x-parser-schema-id which indicates original schema name
3496
+ // But skip this if we're defining the component schema itself (to avoid circular references)
3497
+ if (!isComponentSchemaDefinition && schema['x-parser-schema-id'] && typeof schema['x-parser-schema-id'] === 'string') {
3498
+ const schemaId = schema['x-parser-schema-id'];
3499
+ // Check if this matches a known component schema
3500
+ if (schemaRegistry.has(schemaId)) {
3501
+ const typeName = toTSTypeName(schemaId);
3502
+ return typeName;
3503
+ }
3504
+ }
3505
+ if (!schema.type) {
3506
+ // If no type specified, check for properties (object) or items (array)
3507
+ if (schema.properties) {
3508
+ schema.type = 'object';
3509
+ } else if (schema.items) {
3510
+ schema.type = 'array';
3511
+ } else {
3512
+ return 'any';
3513
+ }
3514
+ }
3515
+ switch (schema.type) {
3516
+ case 'string':
3517
+ if (schema.enum && schema.enum.length > 0) {
3518
+ return schema.enum.map(val => `'${val}'`).join(' | ');
3519
+ }
3520
+ return 'string';
3521
+ case 'integer':
3522
+ case 'number':
3523
+ return 'number';
3524
+ case 'boolean':
3525
+ return 'boolean';
3526
+ case 'array':
3527
+ {
3528
+ if (schema.items) {
3529
+ const itemType = jsonSchemaToTypeScriptType(schema.items, fieldName);
3530
+ return `${itemType}[]`;
3531
+ }
3532
+ return 'any[]';
3533
+ }
3534
+ case 'object':
3535
+ // For objects with properties, we should generate inline types or check if it's a known schema
3536
+ if (schema.properties) {
3537
+ // This is a complex object - for now return Record<string, any>
3538
+ // In a more sophisticated implementation, we could generate inline types
3539
+ return 'Record<string, any>';
3540
+ }
3541
+ return 'Record<string, any>';
3542
+ default:
3543
+ return 'any';
3544
+ }
3545
+ }
3546
+
3547
+ // Generate message interfaces
3548
+ function generateMessageInterface(schema, messageName) {
3549
+ if (!schema || !schema.properties) {
3550
+ return ' [key: string]: any;';
3551
+ }
3552
+ const fields = Object.entries(schema.properties).map(([fieldName, fieldSchema]) => {
3553
+ const tsType = jsonSchemaToTypeScriptType(fieldSchema, fieldName);
3554
+ const optional = !schema.required || !schema.required.includes(fieldName);
3555
+ const optionalMarker = optional ? '?' : '';
3556
+ let fieldDoc = '';
3557
+ if (fieldSchema.description) {
3558
+ fieldDoc = ` /** ${fieldSchema.description} */\n`;
3559
+ }
3560
+ return `${fieldDoc} ${fieldName}${optionalMarker}: ${tsType};`;
3561
+ }).join('\n');
3562
+ return fields;
3563
+ }
3564
+
3565
+ // Return the processed data and generation functions
3566
+ return {
3567
+ messageSchemas,
3568
+ componentSchemas,
3569
+ messageToChannels,
3570
+ generatedTypes,
3571
+ schemaRegistry,
3572
+ // Generation functions
3573
+ generateMessageInterface,
3574
+ jsonSchemaToTypeScriptType,
3575
+ // Generate interfaces for component schemas
3576
+ generateComponentSchemas() {
3577
+ let content = '';
3578
+ componentSchemas.forEach(schema => {
3579
+ const doc = schema.description ? `/** ${schema.description} */\n` : `/** ${schema.name} */\n`;
3580
+
3581
+ // Check if this is a standalone enum schema
3582
+ if (schema.schema.type === 'string' && schema.schema.enum && Array.isArray(schema.schema.enum)) {
3583
+ // Generate union type for enum
3584
+ const enumValues = schema.schema.enum.map(val => `'${val}'`).join(' | ');
3585
+ content += `${doc}export type ${schema.typeName} = ${enumValues};\n\n`;
3586
+ } else if (schema.schema.type && !schema.schema.properties) {
3587
+ // For primitive types (integer, number, string, boolean, array) without properties,
3588
+ // generate a type alias instead of an interface
3589
+ const tsType = jsonSchemaToTypeScriptType(schema.schema, schema.name, true);
3590
+ content += `${doc}export type ${schema.typeName} = ${tsType};\n\n`;
3591
+ } else {
3592
+ // Generate interface for object schema
3593
+ content += `${doc}export interface ${schema.typeName} {\n`;
3594
+ content += generateMessageInterface(schema.schema, schema.typeName);
3595
+ content += '\n}\n\n';
3596
+ }
3597
+
3598
+ // Track generated types to avoid duplicates
3599
+ generatedTypes.add(schema.typeName);
3600
+ });
3601
+ return content;
3602
+ },
3603
+ // Generate interfaces for each message
3604
+ generateMessageSchemas() {
3605
+ let content = '';
3606
+ messageSchemas.forEach(schema => {
3607
+ const interfaceName = `${schema.typeName}Payload`;
3608
+
3609
+ // Check if this is a duplicate of a component schema
3610
+ // For message payloads that match component schema names, skip the payload version
3611
+ if (generatedTypes.has(schema.typeName) || generatedTypes.has(interfaceName)) {
3612
+ // Skip generating the payload version if we already have the component schema
3613
+ return;
3614
+ }
3615
+ const doc = schema.description ? `/** ${schema.description} */\n` : `/** ${schema.name} message payload */\n`;
3616
+ content += `${doc}export interface ${interfaceName} {\n`;
3617
+ content += generateMessageInterface(schema.payload, schema.typeName);
3618
+ content += '\n}\n\n';
3619
+ generatedTypes.add(interfaceName);
3620
+ });
3621
+ return content;
3622
+ },
3623
+ // Generate message type constants and unions
3624
+ generateMessageTypes() {
3625
+ if (!includeMessageTypes || messageSchemas.length === 0) {
3626
+ return '';
3627
+ }
3628
+ let content = '';
3629
+
3630
+ // Generate a union type for all message payloads
3631
+ const payloadTypes = messageSchemas.map(schema => `${schema.typeName}Payload`).join(' | ');
3632
+ content += '/** Union type for all message payloads */\n';
3633
+ content += `export type MessagePayload = ${payloadTypes};\n\n`;
3634
+
3635
+ // Generate message type constants
3636
+ content += '/** Message type constants */\n';
3637
+ content += 'export const MessageTypes = {\n';
3638
+ messageSchemas.forEach(schema => {
3639
+ content += ` ${schema.typeName.toUpperCase()}: '${schema.name}',\n`;
3640
+ });
3641
+ content += '} as const;\n\n';
3642
+ content += '/** Message type union */\n';
3643
+ content += 'export type MessageType = typeof MessageTypes[keyof typeof MessageTypes];\n\n';
3644
+ return content;
3645
+ }
3646
+ };
3647
+ }
3648
+ ;// ../common/src/index.js
3649
+ /**
3650
+ * AsyncAPI Common Template Utilities
3651
+ *
3652
+ * This package provides shared utilities for AsyncAPI template generation,
3653
+ * reducing code duplication across multiple templates.
3654
+ */
3655
+
3656
+ // String utilities
3657
+
3658
+
3659
+ // Message utilities
3660
+
3661
+
3662
+ // Channel utilities
3663
+
3664
+
3665
+ // Security utilities
3666
+
3667
+
3668
+ // Template utilities
3669
+
3670
+
3671
+ // Operation utilities
3672
+
3673
+
3674
+ // Model generation utilities
3675
+
3676
+
3677
+
3678
+
3679
+ // Re-export all utilities
3680
+
3681
+
3682
+ // Version information
3683
+ const VERSION = '1.0.0';
3684
+
3685
+ // Convenience function to get all utilities in one object
3686
+ function getAllUtilities() {
3687
+ return {
3688
+ string: {
3689
+ toRustIdentifier: toRustIdentifier,
3690
+ toRustTypeName: toRustTypeName,
3691
+ toRustFieldName: toRustFieldName,
3692
+ toRustEnumVariant: toRustEnumVariant,
3693
+ toRustEnumVariantWithSerde: toRustEnumVariantWithSerde,
3694
+ toKebabCase: toKebabCase,
3695
+ toPascalCase: toPascalCase,
3696
+ toSnakeCase: toSnakeCase,
3697
+ toCamelCase: toCamelCase
3698
+ },
3699
+ message: {
3700
+ getMessageTypeName: getMessageTypeName,
3701
+ getMessageRustTypeName: getMessageRustTypeName,
3702
+ getPayloadRustTypeName: getPayloadRustTypeName,
3703
+ getMessageTypeScriptTypeName: getMessageTypeScriptTypeName,
3704
+ getPayloadTypeScriptTypeName: getPayloadTypeScriptTypeName,
3705
+ messageHasPayload: messageHasPayload,
3706
+ getMessageContentType: getMessageContentType
3707
+ },
3708
+ channel: {
3709
+ getNatsSubject: getNatsSubject,
3710
+ getChannelAddress: getChannelAddress,
3711
+ isDynamicChannel: isDynamicChannel,
3712
+ extractChannelVariables: extractChannelVariables,
3713
+ extractChannelParameters: extractChannelParameters,
3714
+ getChannelParameters: getChannelParameters,
3715
+ resolveChannelAddress: resolveChannelAddress,
3716
+ channelHasParameters: channelHasParameters,
3717
+ generateChannelParameterArgs: generateChannelParameterArgs,
3718
+ generateChannelFormatting: generateChannelFormatting,
3719
+ generateTypeScriptChannelParameterArgs: generateTypeScriptChannelParameterArgs,
3720
+ generateTypeScriptChannelFormatting: generateTypeScriptChannelFormatting,
3721
+ extractServerNameFromRef: extractServerNameFromRef,
3722
+ analyzeChannelServerMappings: analyzeChannelServerMappings,
3723
+ isChannelAllowedOnServer: isChannelAllowedOnServer
3724
+ },
3725
+ security: {
3726
+ analyzeOperationSecurity: analyzeOperationSecurity,
3727
+ operationHasSecurity: operationHasSecurity,
3728
+ operationRequiresAuth: operationRequiresAuth,
3729
+ hasSecuritySchemes: hasSecuritySchemes,
3730
+ getSecuritySchemeType: getSecuritySchemeType,
3731
+ getSecuritySchemeName: getSecuritySchemeName,
3732
+ getSecuritySchemeLocation: getSecuritySchemeLocation,
3733
+ extractOperationSecurityMap: extractOperationSecurityMap,
3734
+ getAllSecuritySchemes: getAllSecuritySchemes,
3735
+ isSecuritySchemeType: isSecuritySchemeType,
3736
+ getDefaultPort: getDefaultPort,
3737
+ validateChannelServerReferences: validateChannelServerReferences
3738
+ },
3739
+ template: {
3740
+ isTemplateVariable: isTemplateVariable,
3741
+ extractAsyncApiInfo: extractAsyncApiInfo,
3742
+ resolveTemplateParameters: resolveTemplateParameters,
3743
+ generatePackageJson: generatePackageJson,
3744
+ generateReadmeContent: generateReadmeContent,
3745
+ validateTemplateParameters: validateTemplateParameters,
3746
+ generateTypeScriptConfig: generateTypeScriptConfig,
3747
+ generateEslintConfig: generateEslintConfig,
3748
+ formatGenerationDate: formatGenerationDate,
3749
+ generateFileHeader: generateFileHeader
3750
+ },
3751
+ operation: {
3752
+ getOperationName: getOperationName,
3753
+ getOperationAction: getOperationAction,
3754
+ getOperationChannel: getOperationChannel,
3755
+ getOperationMessages: getOperationMessages,
3756
+ isOperationSend: isOperationSend,
3757
+ isOperationReceive: isOperationReceive,
3758
+ getOperationRustFunctionName: getOperationRustFunctionName,
3759
+ getOperationTypeScriptMethodName: getOperationTypeScriptMethodName,
3760
+ getOperationDescription: getOperationDescription,
3761
+ getOperationSummary: getOperationSummary,
3762
+ extractAllOperations: extractAllOperations,
3763
+ groupOperationsByAction: groupOperationsByAction,
3764
+ getOperationTraits: getOperationTraits,
3765
+ operationHasTraits: operationHasTraits,
3766
+ getOperationTags: getOperationTags,
3767
+ generateOperationHandlerName: generateOperationHandlerName
3768
+ },
3769
+ models: {
3770
+ generateRustModels: generateRustModels,
3771
+ generateMessageEnvelope: generateMessageEnvelope,
3772
+ generateTypeScriptModels: generateTypeScriptModels
3773
+ }
3774
+ };
3775
+ }
3776
+ export { VERSION, analyzeChannelServerMappings, analyzeOperationSecurity, channelHasParameters, extractAllOperations, extractAsyncApiInfo, extractChannelParameters, extractChannelVariables, extractOperationSecurityMap, extractServerNameFromRef, formatGenerationDate, generateChannelFormatting, generateChannelParameterArgs, generateEslintConfig, generateFileHeader, generateMessageEnvelope, generateOperationHandlerName, generatePackageJson, generateReadmeContent, generateRustModels, generateTypeScriptChannelFormatting, generateTypeScriptChannelParameterArgs, generateTypeScriptConfig, generateTypeScriptModels, getAllSecuritySchemes, getAllUtilities, getChannelAddress, getChannelParameters, getDefaultPort, getMessageContentType, getMessageRustTypeName, getMessageTypeName, getMessageTypeScriptTypeName, getNatsSubject, getOperationAction, getOperationChannel, getOperationDescription, getOperationMessages, getOperationName, getOperationRustFunctionName, getOperationSummary, getOperationTags, getOperationTraits, getOperationTypeScriptMethodName, getPayloadRustTypeName, getPayloadTypeScriptTypeName, getSecuritySchemeLocation, getSecuritySchemeName, getSecuritySchemeType, groupOperationsByAction, hasSecuritySchemes, isChannelAllowedOnServer, isDynamicChannel, isOperationReceive, isOperationSend, isSecuritySchemeType, isTemplateVariable, messageHasPayload, operationHasSecurity, operationHasTraits, operationRequiresAuth, resolveChannelAddress, resolveTemplateParameters, toCamelCase, toKebabCase, toPascalCase, toRustEnumVariant, toRustEnumVariantWithSerde, toRustFieldName, toRustIdentifier, toRustTypeName, toSnakeCase, validateChannelServerReferences, validateTemplateParameters };