@ioka-technologies/asyncapi-rust-client-template 0.0.21 → 0.0.23

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.
@@ -0,0 +1,3478 @@
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: 'node',
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
+ default:
2309
+ // Default to i32 for unspecified format (maintains backward compatibility)
2310
+ return 'i32';
2311
+ }
2312
+ case 'number':
2313
+ return 'f64';
2314
+ case 'boolean':
2315
+ return 'bool';
2316
+ case 'array':
2317
+ {
2318
+ const itemType = jsonSchemaToRustType(schema.items);
2319
+ return `Vec<${itemType}>`;
2320
+ }
2321
+ case 'object':
2322
+ if (schema.properties && Object.keys(schema.properties).length > 0) {
2323
+ // Generate nested struct
2324
+ if (typeName) {
2325
+ const structName = toRustTypeName(typeName);
2326
+ if (!generatedTypes.has(structName)) {
2327
+ generatedTypes.add(structName);
2328
+ nestedSchemas.set(structName, {
2329
+ type: 'struct',
2330
+ schema: schema,
2331
+ description: schema.description
2332
+ });
2333
+ }
2334
+ return structName;
2335
+ }
2336
+ }
2337
+ return 'serde_json::Value';
2338
+ default:
2339
+ return 'serde_json::Value';
2340
+ }
2341
+ }
2342
+
2343
+ // Generate message structs
2344
+ function generateMessageStruct(schema, messageName) {
2345
+ if (!schema || !schema.properties) {
2346
+ return ' pub data: serde_json::Value,';
2347
+ }
2348
+ const fields = Object.entries(schema.properties).map(([fieldName, fieldSchema]) => {
2349
+ const rustFieldName = toRustFieldName(fieldName);
2350
+ const fieldTypeName = `${messageName}${toRustTypeName(fieldName)}`;
2351
+ const rustType = jsonSchemaToRustType(fieldSchema, fieldTypeName);
2352
+ const requiredFields = schema.required;
2353
+ const optional = !requiredFields || !Array.isArray(requiredFields) || requiredFields.indexOf(fieldName) === -1;
2354
+ const finalType = optional ? `Option<${rustType}>` : rustType;
2355
+ let fieldDoc = '';
2356
+ if (fieldSchema.description) {
2357
+ fieldDoc = ` /// ${fieldSchema.description}\n`;
2358
+ }
2359
+ let serdeRename = '';
2360
+ if (rustFieldName !== fieldName) {
2361
+ serdeRename = ` #[serde(rename = "${fieldName}")]\n`;
2362
+ }
2363
+ let skipSerializing = '';
2364
+ if (optional) {
2365
+ skipSerializing = ' #[serde(skip_serializing_if = "Option::is_none")]\n';
2366
+ }
2367
+ return `${fieldDoc}${serdeRename}${skipSerializing} pub ${rustFieldName}: ${finalType},`;
2368
+ }).join('\n');
2369
+ return fields;
2370
+ }
2371
+
2372
+ // Process all component schemas first to ensure they are available for references
2373
+ componentSchemas.forEach(schema => {
2374
+ jsonSchemaToRustType(schema.schema, schema.rustName);
2375
+ generatedTypes.add(schema.rustName);
2376
+ });
2377
+
2378
+ // Process all message schemas to ensure all referenced types are generated
2379
+ messageSchemas.forEach(schema => {
2380
+ if (schema.payload) {
2381
+ jsonSchemaToRustType(schema.payload, schema.rustName);
2382
+ }
2383
+ });
2384
+
2385
+ // Return the processed data and generation functions
2386
+ return {
2387
+ messageSchemas,
2388
+ componentSchemas,
2389
+ messageToChannels,
2390
+ generatedTypes,
2391
+ nestedSchemas,
2392
+ schemaRegistry,
2393
+ // Generation functions
2394
+ generateMessageStruct,
2395
+ jsonSchemaToRustType,
2396
+ // Generate component schema definitions
2397
+ generateComponentSchemas() {
2398
+ let result = '';
2399
+ componentSchemas.forEach(schema => {
2400
+ const doc = schema.description ? `/// ${schema.description}\n` : `/// ${schema.name}\n`;
2401
+
2402
+ // Check if this is a standalone enum schema
2403
+ if (schema.schema.type === 'string' && schema.schema.enum && Array.isArray(schema.schema.enum)) {
2404
+ // Generate enum definition with serde rename attributes for lowercase serialization
2405
+ const variants = schema.schema.enum.map(variant => {
2406
+ const {
2407
+ rustName,
2408
+ serializedName
2409
+ } = toRustEnumVariantWithSerde(variant);
2410
+ return ` #[serde(rename = "${serializedName}")]\n ${rustName}`;
2411
+ }).join(',\n');
2412
+ result += `
2413
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2414
+ pub enum ${schema.rustName} {
2415
+ ${variants},
2416
+ }
2417
+ `;
2418
+ } else {
2419
+ // Generate struct definition
2420
+ const fields = generateMessageStruct(schema.schema, schema.rustName);
2421
+ result += `
2422
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize)]
2423
+ pub struct ${schema.rustName} {
2424
+ ${fields}
2425
+ }
2426
+ `;
2427
+ }
2428
+ });
2429
+ return result;
2430
+ },
2431
+ // Generate nested type definitions
2432
+ generateNestedTypes() {
2433
+ let result = '';
2434
+ for (const [typeName, typeInfo] of nestedSchemas.entries()) {
2435
+ // Don't skip enums - they need to be generated even if the parent type exists
2436
+ const isEnum = typeInfo.type === 'enum';
2437
+ const isComponentSchema = componentSchemas.some(cs => cs.rustName === typeName);
2438
+
2439
+ // Skip if this type was already generated as a component schema (but not enums)
2440
+ if (!isEnum && isComponentSchema) {
2441
+ continue;
2442
+ }
2443
+ if (typeInfo.type === 'enum') {
2444
+ const variants = typeInfo.variants.map(variant => {
2445
+ const {
2446
+ rustName,
2447
+ serializedName
2448
+ } = toRustEnumVariantWithSerde(variant);
2449
+ return ` #[serde(rename = "${serializedName}")]\n ${rustName}`;
2450
+ }).join(',\n');
2451
+ const doc = typeInfo.description ? `/// ${typeInfo.description}\n` : '';
2452
+ result += `
2453
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2454
+ pub enum ${typeName} {
2455
+ ${variants},
2456
+ }
2457
+ `;
2458
+ } else if (typeInfo.type === 'struct') {
2459
+ const fields = generateMessageStruct(typeInfo.schema, typeName);
2460
+ const doc = typeInfo.description ? `/// ${typeInfo.description}\n` : '';
2461
+ result += `
2462
+ ${doc}#[derive(Debug, Clone, Serialize, Deserialize)]
2463
+ pub struct ${typeName} {
2464
+ ${fields}
2465
+ }
2466
+ `;
2467
+ }
2468
+ }
2469
+ return result;
2470
+ },
2471
+ // Generate AsyncApiMessage trait implementations (optional)
2472
+ generateAsyncApiTrait() {
2473
+ if (!includeAsyncApiTrait) {
2474
+ return '';
2475
+ }
2476
+
2477
+ // Track which types have already had AsyncApiMessage implementations generated
2478
+ const implementedTypes = new Set();
2479
+ const implementations = [];
2480
+
2481
+ // First add the trait definition
2482
+ implementations.push(`
2483
+ /// Base trait for all AsyncAPI messages providing runtime type information
2484
+ ///
2485
+ /// This trait enables:
2486
+ /// - **Dynamic message routing**: Route messages based on their type at runtime
2487
+ /// - **Channel identification**: Determine which channel a message belongs to
2488
+ /// - **Logging and monitoring**: Track message types for observability
2489
+ /// - **Protocol abstraction**: Handle different message types uniformly
2490
+ pub trait AsyncApiMessage {
2491
+ /// Returns the message type identifier as defined in the AsyncAPI specification
2492
+ ///
2493
+ /// This is used for:
2494
+ /// - Message routing and dispatch
2495
+ /// - Logging and monitoring
2496
+ /// - Protocol-level message identification
2497
+ fn message_type(&self) -> &'static str;
2498
+
2499
+ /// Returns the primary channel this message is associated with
2500
+ ///
2501
+ /// Used for:
2502
+ /// - Default routing when channel is not explicitly specified
2503
+ /// - Message categorization and organization
2504
+ /// - Channel-based access control and filtering
2505
+ fn channel(&self) -> &'static str;
2506
+ }`);
2507
+ messageSchemas.forEach(schema => {
2508
+ const doc = schema.description ? `/// ${schema.description}` : `/// ${schema.name} message`;
2509
+ const primaryChannel = schema.channels.length > 0 ? schema.channels[0] : 'default';
2510
+
2511
+ // Check if the message payload references a component schema
2512
+ let payloadRustName = null;
2513
+ let isComponentMessage = false;
2514
+ if (schema.rawPayload && schema.rawPayload.$ref) {
2515
+ const refName = schema.rawPayload.$ref.split('/').pop();
2516
+ payloadRustName = toRustTypeName(refName);
2517
+ isComponentMessage = true;
2518
+ } else if (schema.payload && schema.payload.$ref) {
2519
+ const refName = schema.payload.$ref.split('/').pop();
2520
+ payloadRustName = toRustTypeName(refName);
2521
+ isComponentMessage = true;
2522
+ } else if (schema.payload && schema.payload['x-parser-schema-id']) {
2523
+ // Handle resolved $ref references
2524
+ const schemaId = schema.payload['x-parser-schema-id'];
2525
+ if (schemaRegistry.has(schemaId)) {
2526
+ payloadRustName = toRustTypeName(schemaId);
2527
+ isComponentMessage = true;
2528
+ }
2529
+ }
2530
+
2531
+ // For component messages, always generate the message wrapper type
2532
+ // even if the payload schema already exists
2533
+ if (isComponentMessage && payloadRustName && !implementedTypes.has(schema.rustName)) {
2534
+ implementedTypes.add(schema.rustName);
2535
+ implementations.push(`
2536
+ ${doc}
2537
+ #[derive(Debug, Clone, Serialize, Deserialize)]
2538
+ pub struct ${schema.rustName} {
2539
+ #[serde(flatten)]
2540
+ pub payload: ${payloadRustName},
2541
+ }
2542
+
2543
+ impl AsyncApiMessage for ${schema.rustName} {
2544
+ fn message_type(&self) -> &'static str {
2545
+ "${schema.name}"
2546
+ }
2547
+
2548
+ fn channel(&self) -> &'static str {
2549
+ "${primaryChannel}"
2550
+ }
2551
+ }`);
2552
+ } else if (!generatedTypes.has(schema.rustName) && !implementedTypes.has(schema.rustName)) {
2553
+ // Generate both struct and implementation for inline message schemas
2554
+ implementedTypes.add(schema.rustName);
2555
+ implementations.push(`
2556
+ ${doc}
2557
+ #[derive(Debug, Clone, Serialize, Deserialize)]
2558
+ pub struct ${schema.rustName} {
2559
+ ${generateMessageStruct(schema.payload, schema.rustName)}
2560
+ }
2561
+
2562
+ impl AsyncApiMessage for ${schema.rustName} {
2563
+ fn message_type(&self) -> &'static str {
2564
+ "${schema.name}"
2565
+ }
2566
+
2567
+ fn channel(&self) -> &'static str {
2568
+ "${primaryChannel}"
2569
+ }
2570
+ }`);
2571
+ } else if (payloadRustName && generatedTypes.has(payloadRustName) && !implementedTypes.has(payloadRustName)) {
2572
+ // Generate AsyncApiMessage implementation for existing component schema
2573
+ implementedTypes.add(payloadRustName);
2574
+ implementations.push(`
2575
+ impl AsyncApiMessage for ${payloadRustName} {
2576
+ fn message_type(&self) -> &'static str {
2577
+ "${schema.name}"
2578
+ }
2579
+
2580
+ fn channel(&self) -> &'static str {
2581
+ "${primaryChannel}"
2582
+ }
2583
+ }`);
2584
+ }
2585
+ });
2586
+ return implementations.join('');
2587
+ }
2588
+ };
2589
+ }
2590
+ ;// ../common/src/envelope-rust.js
2591
+ /* eslint-disable no-unused-vars */
2592
+
2593
+
2594
+ /**
2595
+ * Generate a unified MessageEnvelope for both rust-server and rust-client templates
2596
+ * This envelope includes all features needed by both templates:
2597
+ * - Basic message structure (id, operation, payload, timestamp)
2598
+ * - Request/response patterns (correlation_id, create_response)
2599
+ * - Channel routing (channel field)
2600
+ * - Error handling (error field, error methods)
2601
+ * - Authentication (auth header methods)
2602
+ * - Serialization (to_bytes, from_bytes)
2603
+ */
2604
+ function generateMessageEnvelope() {
2605
+ return `use serde::{de::DeserializeOwned, Deserialize, Serialize};
2606
+ use std::collections::HashMap;
2607
+ use uuid::Uuid;
2608
+
2609
+ /// Unified message envelope for consistent AsyncAPI message format
2610
+ ///
2611
+ /// This envelope provides a standardized structure for all messages sent through the system,
2612
+ /// enabling better correlation, error handling, authentication, and observability.
2613
+ ///
2614
+ /// ## Features
2615
+ ///
2616
+ /// - **Request/Response Patterns**: Correlation IDs for matching requests with responses
2617
+ /// - **Error Handling**: Built-in error information for failed operations
2618
+ /// - **Authentication**: Integrated auth header support
2619
+ /// - **Channel Routing**: Optional channel context for message routing
2620
+ /// - **Serialization**: Efficient byte conversion for transport layers
2621
+ /// - **Type Safety**: Strongly-typed payload extraction
2622
+ ///
2623
+ /// ## Usage
2624
+ ///
2625
+ /// \`\`\`no-run
2626
+ /// use crate::models::*;
2627
+ /// use uuid::Uuid;
2628
+ /// use std::collections::HashMap;
2629
+ ///
2630
+ /// // Create a basic message envelope
2631
+ /// let envelope = MessageEnvelope::new("sendChatMessage", chat_message)?;
2632
+ ///
2633
+ /// // Create with correlation ID for request/response
2634
+ /// let request = MessageEnvelope::new_with_correlation_id(
2635
+ /// "getUserProfile",
2636
+ /// user_request,
2637
+ /// Uuid::new_v4().to_string()
2638
+ /// )?;
2639
+ ///
2640
+ /// // Create response with same correlation ID
2641
+ /// let response = request.create_response("getUserProfile_response", user_profile)?;
2642
+ ///
2643
+ /// // Create error response
2644
+ /// let error = MessageEnvelope::error_response(
2645
+ /// "getUserProfile_response",
2646
+ /// "USER_NOT_FOUND",
2647
+ /// "User does not exist",
2648
+ /// request.correlation_id().map(|s| s.to_string())
2649
+ /// );
2650
+ ///
2651
+ /// // Add authentication headers
2652
+ /// let mut headers = HashMap::new();
2653
+ /// headers.insert("Authorization".to_string(), "Bearer token123".to_string());
2654
+ /// let auth_envelope = envelope.with_headers(headers);
2655
+ ///
2656
+ /// // Serialize for transport
2657
+ /// let bytes = envelope.to_bytes()?;
2658
+ /// let deserialized = MessageEnvelope::from_bytes(&bytes)?;
2659
+ /// \`\`\`
2660
+
2661
+ /// Standard message envelope for all AsyncAPI messages
2662
+ ///
2663
+ /// This envelope provides a consistent structure for all messages sent through the system,
2664
+ /// enabling better correlation, error handling, and observability.
2665
+ #[derive(Debug, Clone, Serialize, Deserialize)]
2666
+ pub struct MessageEnvelope {
2667
+ /// Unique message identifier
2668
+ pub id: String,
2669
+ /// AsyncAPI operation ID
2670
+ pub operation: String,
2671
+ /// Message payload (any serializable type)
2672
+ pub payload: serde_json::Value,
2673
+ /// ISO 8601 timestamp when message was created
2674
+ pub timestamp: String,
2675
+ /// Correlation ID for request/response patterns
2676
+ pub correlation_id: Option<String>,
2677
+ /// Optional channel context for routing
2678
+ pub channel: Option<String>,
2679
+ /// Transport-level headers (auth, routing, etc.)
2680
+ pub headers: Option<HashMap<String, String>>,
2681
+ /// Error information if applicable
2682
+ pub error: Option<MessageError>,
2683
+ }
2684
+
2685
+ /// Error information for failed operations
2686
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2687
+ pub struct MessageError {
2688
+ /// Error code (e.g., "VALIDATION_ERROR", "TIMEOUT", "UNAUTHORIZED")
2689
+ pub code: String,
2690
+ /// Human-readable error message
2691
+ pub message: String,
2692
+ }
2693
+
2694
+ impl MessageEnvelope {
2695
+ /// Create a new message envelope with the given operation and payload
2696
+ pub fn new<T: Serialize>(operation: &str, payload: T) -> Result<Self, serde_json::Error> {
2697
+ Ok(Self {
2698
+ id: Uuid::new_v4().to_string(),
2699
+ operation: operation.to_string(),
2700
+ payload: serde_json::to_value(payload)?,
2701
+ timestamp: chrono::Utc::now().to_rfc3339(),
2702
+ correlation_id: None,
2703
+ channel: None,
2704
+ headers: None,
2705
+ error: None,
2706
+ })
2707
+ }
2708
+
2709
+ /// Create a new envelope with automatic correlation ID generation
2710
+ pub fn new_with_id<T: Serialize>(
2711
+ operation: &str,
2712
+ payload: T,
2713
+ ) -> Result<Self, serde_json::Error> {
2714
+ Self::new(operation, payload)
2715
+ .map(|envelope| envelope.with_correlation_id(Uuid::new_v4().to_string()))
2716
+ }
2717
+
2718
+ /// Create a new message envelope with a specific correlation ID
2719
+ pub fn new_with_correlation_id<T: Serialize>(
2720
+ operation: &str,
2721
+ payload: T,
2722
+ correlation_id: String,
2723
+ ) -> Result<Self, serde_json::Error> {
2724
+ let mut envelope = Self::new(operation, payload)?;
2725
+ envelope.correlation_id = Some(correlation_id);
2726
+ Ok(envelope)
2727
+ }
2728
+
2729
+ /// Create an error response envelope
2730
+ pub fn error_response(
2731
+ operation: &str,
2732
+ error_code: &str,
2733
+ error_message: &str,
2734
+ correlation_id: Option<String>,
2735
+ ) -> Self {
2736
+ Self {
2737
+ id: Uuid::new_v4().to_string(),
2738
+ operation: operation.to_string(),
2739
+ payload: serde_json::Value::Null,
2740
+ timestamp: chrono::Utc::now().to_rfc3339(),
2741
+ correlation_id,
2742
+ channel: None,
2743
+ headers: None,
2744
+ error: Some(MessageError {
2745
+ code: error_code.to_string(),
2746
+ message: error_message.to_string(),
2747
+ }),
2748
+ }
2749
+ }
2750
+
2751
+ /// Set the correlation ID for this envelope
2752
+ pub fn with_correlation_id(mut self, id: String) -> Self {
2753
+ self.correlation_id = Some(id);
2754
+ self
2755
+ }
2756
+
2757
+ /// Set the channel for this envelope
2758
+ pub fn with_channel(mut self, channel: String) -> Self {
2759
+ self.channel = Some(channel);
2760
+ self
2761
+ }
2762
+
2763
+ /// Set headers for this envelope
2764
+ pub fn with_headers(mut self, headers: HashMap<String, String>) -> Self {
2765
+ self.headers = Some(headers);
2766
+ self
2767
+ }
2768
+
2769
+ /// Add a single header to this envelope
2770
+ pub fn with_header(mut self, key: String, value: String) -> Self {
2771
+ if self.headers.is_none() {
2772
+ self.headers = Some(HashMap::new());
2773
+ }
2774
+ if let Some(ref mut headers) = self.headers {
2775
+ headers.insert(key, value);
2776
+ }
2777
+ self
2778
+ }
2779
+
2780
+ /// Add authentication headers to the envelope
2781
+ /// This method accepts any headers map, allowing templates to integrate their own auth systems
2782
+ pub fn with_auth_headers(mut self, auth_headers: HashMap<String, String>) -> Self {
2783
+ if !auth_headers.is_empty() {
2784
+ if let Some(ref mut headers) = self.headers {
2785
+ headers.extend(auth_headers);
2786
+ } else {
2787
+ self.headers = Some(auth_headers);
2788
+ }
2789
+ }
2790
+ self
2791
+ }
2792
+
2793
+ /// Set an error on this envelope
2794
+ pub fn with_error(mut self, code: &str, message: &str) -> Self {
2795
+ self.error = Some(MessageError {
2796
+ code: code.to_string(),
2797
+ message: message.to_string(),
2798
+ });
2799
+ self
2800
+ }
2801
+
2802
+ /// Extract the payload as a strongly-typed message
2803
+ pub fn extract_payload<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
2804
+ serde_json::from_value(self.payload.clone())
2805
+ }
2806
+
2807
+ /// Check if this envelope contains an error
2808
+ pub fn is_error(&self) -> bool {
2809
+ self.error.is_some()
2810
+ }
2811
+
2812
+ /// Get the correlation ID if present
2813
+ pub fn correlation_id(&self) -> Option<&str> {
2814
+ self.correlation_id.as_deref()
2815
+ }
2816
+
2817
+ /// Create a response envelope with the same correlation ID
2818
+ pub fn create_response<T: Serialize>(
2819
+ &self,
2820
+ response_operation: &str,
2821
+ payload: T,
2822
+ ) -> Result<Self, serde_json::Error> {
2823
+ let mut response = Self::new(response_operation, payload)?;
2824
+ response.correlation_id = self.correlation_id.clone();
2825
+ response.channel = self.channel.clone();
2826
+ Ok(response)
2827
+ }
2828
+
2829
+ /// Convert the envelope to bytes for transport
2830
+ pub fn to_bytes(&self) -> Result<Vec<u8>, serde_json::Error> {
2831
+ serde_json::to_vec(self)
2832
+ }
2833
+
2834
+ /// Parse envelope from bytes received from transport
2835
+ pub fn from_bytes(bytes: &[u8]) -> Result<Self, serde_json::Error> {
2836
+ serde_json::from_slice(bytes)
2837
+ }
2838
+ }
2839
+
2840
+ #[cfg(test)]
2841
+ mod tests {
2842
+ use super::*;
2843
+ use serde::{Deserialize, Serialize};
2844
+
2845
+ #[derive(Debug, Serialize, Deserialize, PartialEq)]
2846
+ struct TestPayload {
2847
+ message: String,
2848
+ count: u32,
2849
+ }
2850
+
2851
+ #[test]
2852
+ fn test_envelope_creation() {
2853
+ let payload = TestPayload {
2854
+ message: "test".to_string(),
2855
+ count: 42,
2856
+ };
2857
+
2858
+ let envelope = MessageEnvelope::new("test_operation", &payload).unwrap();
2859
+
2860
+ assert_eq!(envelope.operation, "test_operation");
2861
+ assert!(!envelope.id.is_empty());
2862
+ assert!(!envelope.timestamp.is_empty());
2863
+ assert_eq!(envelope.correlation_id, None);
2864
+ assert_eq!(envelope.error, None);
2865
+
2866
+ let extracted: TestPayload = envelope.extract_payload().unwrap();
2867
+ assert_eq!(extracted, payload);
2868
+ }
2869
+
2870
+ #[test]
2871
+ fn test_envelope_with_correlation_id() {
2872
+ let payload = TestPayload {
2873
+ message: "test".to_string(),
2874
+ count: 42,
2875
+ };
2876
+
2877
+ let correlation_id = "test-correlation-id".to_string();
2878
+ let envelope = MessageEnvelope::new_with_correlation_id(
2879
+ "test_operation",
2880
+ &payload,
2881
+ correlation_id.clone(),
2882
+ ).unwrap();
2883
+
2884
+ assert_eq!(envelope.correlation_id, Some(correlation_id));
2885
+ }
2886
+
2887
+ #[test]
2888
+ fn test_error_response() {
2889
+ let error_envelope = MessageEnvelope::error_response(
2890
+ "test_operation_response",
2891
+ "TEST_ERROR",
2892
+ "Test error message",
2893
+ Some("correlation-123".to_string()),
2894
+ );
2895
+
2896
+ assert!(error_envelope.is_error());
2897
+ assert_eq!(error_envelope.correlation_id, Some("correlation-123".to_string()));
2898
+ if let Some(error) = &error_envelope.error {
2899
+ assert_eq!(error.code, "TEST_ERROR");
2900
+ assert_eq!(error.message, "Test error message");
2901
+ }
2902
+ }
2903
+
2904
+ #[test]
2905
+ fn test_envelope_serialization() {
2906
+ let payload = TestPayload {
2907
+ message: "test".to_string(),
2908
+ count: 42,
2909
+ };
2910
+
2911
+ let envelope = MessageEnvelope::new("test_operation", &payload).unwrap();
2912
+ let bytes = envelope.to_bytes().unwrap();
2913
+ let deserialized = MessageEnvelope::from_bytes(&bytes).unwrap();
2914
+
2915
+ assert_eq!(envelope.id, deserialized.id);
2916
+ assert_eq!(envelope.operation, deserialized.operation);
2917
+ assert_eq!(envelope.timestamp, deserialized.timestamp);
2918
+ }
2919
+
2920
+ #[test]
2921
+ fn test_response_creation() {
2922
+ let request_payload = TestPayload {
2923
+ message: "request".to_string(),
2924
+ count: 1,
2925
+ };
2926
+
2927
+ let response_payload = TestPayload {
2928
+ message: "response".to_string(),
2929
+ count: 2,
2930
+ };
2931
+
2932
+ let request = MessageEnvelope::new_with_correlation_id(
2933
+ "test_request",
2934
+ &request_payload,
2935
+ "test-correlation".to_string(),
2936
+ ).unwrap();
2937
+
2938
+ let response = request.create_response("test_response", &response_payload).unwrap();
2939
+
2940
+ assert_eq!(response.operation, "test_response");
2941
+ assert_eq!(response.correlation_id, request.correlation_id);
2942
+
2943
+ let extracted: TestPayload = response.extract_payload().unwrap();
2944
+ assert_eq!(extracted, response_payload);
2945
+ }
2946
+
2947
+ #[test]
2948
+ fn test_headers_and_auth() {
2949
+ let payload = TestPayload {
2950
+ message: "test".to_string(),
2951
+ count: 42,
2952
+ };
2953
+
2954
+ let mut auth_headers = HashMap::new();
2955
+ auth_headers.insert("Authorization".to_string(), "Bearer token123".to_string());
2956
+
2957
+ let envelope = MessageEnvelope::new("test_operation", &payload)
2958
+ .unwrap()
2959
+ .with_auth_headers(auth_headers)
2960
+ .with_header("Custom-Header".to_string(), "custom-value".to_string());
2961
+
2962
+ assert!(envelope.headers.is_some());
2963
+ let headers = envelope.headers.unwrap();
2964
+ assert_eq!(headers.get("Authorization"), Some(&"Bearer token123".to_string()));
2965
+ assert_eq!(headers.get("Custom-Header"), Some(&"custom-value".to_string()));
2966
+ }
2967
+ }
2968
+ `;
2969
+ }
2970
+ ;// ../common/src/models-ts.js
2971
+ /* eslint-disable no-unused-vars */
2972
+
2973
+
2974
+ /**
2975
+ * Generate TypeScript models from AsyncAPI specification
2976
+ * This helper extracts the common schema processing logic used by TypeScript templates
2977
+ *
2978
+ * @param {Object} asyncapi - AsyncAPI document
2979
+ * @param {Object} options - Generation options
2980
+ * @param {Function} options.toTypeScriptTypeName - Function to convert names to TypeScript type names
2981
+ * @param {Function} options.toTypeScriptIdentifier - Function to convert names to TypeScript identifiers
2982
+ * @param {boolean} options.includeMessageTypes - Whether to include message type constants
2983
+ * @returns {Object} Generated models data and functions
2984
+ */
2985
+ function generateTypeScriptModels(asyncapi, options = {}) {
2986
+ const {
2987
+ toTypeScriptTypeName,
2988
+ toTypeScriptIdentifier,
2989
+ includeMessageTypes = true
2990
+ } = options;
2991
+
2992
+ // Helper functions for TypeScript identifier generation
2993
+ function defaultToTypeScriptIdentifier(str) {
2994
+ if (!str) return 'unknown';
2995
+ let identifier = str.replace(/[^a-zA-Z0-9_]/g, '_').replace(/^[0-9]/, '_$&').replace(/_+/g, '_').replace(/^_+|_+$/g, '');
2996
+ if (/^[0-9]/.test(identifier)) {
2997
+ identifier = 'item_' + identifier;
2998
+ }
2999
+ if (!identifier) {
3000
+ identifier = 'unknown';
3001
+ }
3002
+ return identifier;
3003
+ }
3004
+ function defaultToTypeScriptTypeName(str) {
3005
+ if (!str) return 'Unknown';
3006
+ // Handle camelCase and PascalCase properly
3007
+ const identifier = str.replace(/[^a-zA-Z0-9]/g, '_').replace(/^[0-9]/, '_$&').replace(/_+/g, '_').replace(/^_+|_+$/g, '');
3008
+
3009
+ // Split on underscores and camelCase boundaries
3010
+ const parts = identifier.split(/[_\s]+|(?=[A-Z])/);
3011
+ return parts.filter(part => part.length > 0).map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('');
3012
+ }
3013
+
3014
+ // Use provided functions or defaults
3015
+ const toTSTypeName = toTypeScriptTypeName || defaultToTypeScriptTypeName;
3016
+ const toTSIdentifier = toTypeScriptIdentifier || defaultToTypeScriptIdentifier;
3017
+
3018
+ // Extract message schemas and build channel mapping
3019
+ const components = asyncapi.components();
3020
+ const messageSchemas = [];
3021
+ const componentSchemas = [];
3022
+ const messageToChannels = new Map();
3023
+ const generatedTypes = new Set();
3024
+ const schemaRegistry = new Map();
3025
+
3026
+ // Build schema registry from components.schemas
3027
+ // Try to access the raw AsyncAPI document
3028
+ let rawDoc = null;
3029
+ try {
3030
+ if (asyncapi.json && typeof asyncapi.json === 'function') {
3031
+ rawDoc = asyncapi.json();
3032
+ } else if (asyncapi._json) {
3033
+ rawDoc = asyncapi._json;
3034
+ }
3035
+ } catch (e) {
3036
+ // Ignore
3037
+ }
3038
+
3039
+ // Extract schemas from raw document if available
3040
+ if (rawDoc && rawDoc.components && rawDoc.components.schemas) {
3041
+ Object.entries(rawDoc.components.schemas).forEach(([name, schema]) => {
3042
+ if (name && typeof name === 'string' && schema && typeof schema === 'object') {
3043
+ schemaRegistry.set(name, schema);
3044
+ componentSchemas.push({
3045
+ name,
3046
+ typeName: toTSTypeName(name),
3047
+ schema: schema,
3048
+ description: schema.description
3049
+ });
3050
+ }
3051
+ });
3052
+ }
3053
+
3054
+ // Fallback: try the components.schemas() method
3055
+ if (componentSchemas.length === 0 && components && components.schemas) {
3056
+ try {
3057
+ const schemas = components.schemas();
3058
+ if (schemas) {
3059
+ // Try different ways to access schemas
3060
+ let schemaEntries = [];
3061
+ if (schemas instanceof Map) {
3062
+ schemaEntries = Array.from(schemas.entries());
3063
+ } else if (typeof schemas === 'object') {
3064
+ schemaEntries = Object.entries(schemas);
3065
+ } else if (schemas.all && typeof schemas.all === 'function') {
3066
+ // AsyncAPI parser might have an all() method
3067
+ const allSchemas = schemas.all();
3068
+ if (Array.isArray(allSchemas)) {
3069
+ schemaEntries = allSchemas.map(schema => {
3070
+ const name = schema.uid ? schema.uid() : schema.id ? schema.id() : null;
3071
+ return [name, schema];
3072
+ }).filter(([name]) => name);
3073
+ }
3074
+ }
3075
+ schemaEntries.forEach(([name, schema]) => {
3076
+ // Skip internal AsyncAPI parser objects and numeric keys
3077
+ if (!name || name === 'collections' || name === '_meta' || name.startsWith('_') || /^\d+$/.test(name)) {
3078
+ return;
3079
+ }
3080
+ let schemaData = null;
3081
+ let description = null;
3082
+ try {
3083
+ // Handle different schema object types
3084
+ if (schema && typeof schema.json === 'function') {
3085
+ schemaData = schema.json();
3086
+ } else if (schema && typeof schema === 'object') {
3087
+ schemaData = schema;
3088
+ }
3089
+ if (schema && typeof schema.description === 'function') {
3090
+ description = schema.description();
3091
+ } else if (schema && schema.description) {
3092
+ description = schema.description;
3093
+ }
3094
+ } catch (e) {
3095
+ // Ignore schema extraction errors
3096
+ console.warn(`Failed to extract schema for ${name}:`, e.message);
3097
+ }
3098
+ if (schemaData && typeof name === 'string' && name.length > 0) {
3099
+ schemaRegistry.set(name, schemaData);
3100
+ componentSchemas.push({
3101
+ name,
3102
+ typeName: toTSTypeName(name),
3103
+ schema: schemaData,
3104
+ description
3105
+ });
3106
+ }
3107
+ });
3108
+ }
3109
+ } catch (e) {
3110
+ console.warn('Failed to extract component schemas:', e.message);
3111
+ }
3112
+ }
3113
+
3114
+ // First, build channel to message mapping
3115
+ if (asyncapi.channels) {
3116
+ const channels = asyncapi.channels();
3117
+ if (channels) {
3118
+ Object.entries(channels).forEach(([channelName, channel]) => {
3119
+ try {
3120
+ // Handle AsyncAPI 3.x format
3121
+ if (channel.messages) {
3122
+ const messages = channel.messages();
3123
+ if (messages) {
3124
+ Object.entries(messages).forEach(([msgKey, message]) => {
3125
+ if (message) {
3126
+ let messageName = null;
3127
+ if (message.$ref) {
3128
+ messageName = message.$ref.split('/').pop();
3129
+ } else if (message.name) {
3130
+ messageName = typeof message.name === 'function' ? message.name() : message.name;
3131
+ }
3132
+ if (messageName) {
3133
+ if (!messageToChannels.has(messageName)) {
3134
+ messageToChannels.set(messageName, []);
3135
+ }
3136
+ messageToChannels.get(messageName).push(channelName);
3137
+ }
3138
+ }
3139
+ });
3140
+ }
3141
+ }
3142
+ } catch (e) {
3143
+ // Ignore channel processing errors
3144
+ }
3145
+ });
3146
+ }
3147
+ }
3148
+
3149
+ // Extract messages from components
3150
+ if (components && components.messages) {
3151
+ const messages = components.messages();
3152
+ if (messages) {
3153
+ Object.entries(messages).forEach(([name, message]) => {
3154
+ // Skip internal AsyncAPI parser objects
3155
+ if (name === 'collections' || name === '_meta' || name.startsWith('_')) {
3156
+ return;
3157
+ }
3158
+ let payload = null;
3159
+ let description = null;
3160
+ let title = null;
3161
+ let messageName = name;
3162
+ try {
3163
+ if (message.payload && typeof message.payload === 'function') {
3164
+ const payloadSchema = message.payload();
3165
+ payload = payloadSchema && payloadSchema.json ? payloadSchema.json() : payloadSchema;
3166
+ }
3167
+ description = message.description && typeof message.description === 'function' ? message.description() : message.description;
3168
+ title = message.title && typeof message.title === 'function' ? message.title() : message.title;
3169
+
3170
+ // Try to get the actual message name
3171
+ if (message.name && typeof message.name === 'function') {
3172
+ messageName = message.name();
3173
+ } else if (message.name) {
3174
+ messageName = message.name;
3175
+ }
3176
+ } catch (e) {
3177
+ // Ignore payload extraction errors
3178
+ }
3179
+ const channels = messageToChannels.get(messageName) || messageToChannels.get(name) || [];
3180
+ messageSchemas.push({
3181
+ name: messageName,
3182
+ typeName: toTSTypeName(messageName),
3183
+ payload,
3184
+ description: description || title,
3185
+ channels
3186
+ });
3187
+ });
3188
+ }
3189
+ }
3190
+
3191
+ // Helper function to convert JSON schema to TypeScript type
3192
+ function jsonSchemaToTypeScriptType(schema, fieldName = '') {
3193
+ if (!schema) return 'any';
3194
+
3195
+ // Handle $ref - resolve from schema registry
3196
+ if (schema.$ref) {
3197
+ const refName = schema.$ref.split('/').pop();
3198
+ // Always return the type name for $ref, since we generate all component schemas
3199
+ const typeName = toTSTypeName(refName);
3200
+ return typeName;
3201
+ }
3202
+
3203
+ // Handle resolved $ref - check for x-parser-schema-id which indicates original schema name
3204
+ if (schema['x-parser-schema-id'] && typeof schema['x-parser-schema-id'] === 'string') {
3205
+ const schemaId = schema['x-parser-schema-id'];
3206
+ // Check if this matches a known component schema
3207
+ if (schemaRegistry.has(schemaId)) {
3208
+ const typeName = toTSTypeName(schemaId);
3209
+ return typeName;
3210
+ }
3211
+ }
3212
+ if (!schema.type) {
3213
+ // If no type specified, check for properties (object) or items (array)
3214
+ if (schema.properties) {
3215
+ schema.type = 'object';
3216
+ } else if (schema.items) {
3217
+ schema.type = 'array';
3218
+ } else {
3219
+ return 'any';
3220
+ }
3221
+ }
3222
+ switch (schema.type) {
3223
+ case 'string':
3224
+ if (schema.enum && schema.enum.length > 0) {
3225
+ return schema.enum.map(val => `'${val}'`).join(' | ');
3226
+ }
3227
+ return 'string';
3228
+ case 'integer':
3229
+ case 'number':
3230
+ return 'number';
3231
+ case 'boolean':
3232
+ return 'boolean';
3233
+ case 'array':
3234
+ {
3235
+ if (schema.items) {
3236
+ const itemType = jsonSchemaToTypeScriptType(schema.items, fieldName);
3237
+ return `${itemType}[]`;
3238
+ }
3239
+ return 'any[]';
3240
+ }
3241
+ case 'object':
3242
+ // For objects with properties, we should generate inline types or check if it's a known schema
3243
+ if (schema.properties) {
3244
+ // This is a complex object - for now return Record<string, any>
3245
+ // In a more sophisticated implementation, we could generate inline types
3246
+ return 'Record<string, any>';
3247
+ }
3248
+ return 'Record<string, any>';
3249
+ default:
3250
+ return 'any';
3251
+ }
3252
+ }
3253
+
3254
+ // Generate message interfaces
3255
+ function generateMessageInterface(schema, messageName) {
3256
+ if (!schema || !schema.properties) {
3257
+ return ' [key: string]: any;';
3258
+ }
3259
+ const fields = Object.entries(schema.properties).map(([fieldName, fieldSchema]) => {
3260
+ const tsType = jsonSchemaToTypeScriptType(fieldSchema, fieldName);
3261
+ const optional = !schema.required || !schema.required.includes(fieldName);
3262
+ const optionalMarker = optional ? '?' : '';
3263
+ let fieldDoc = '';
3264
+ if (fieldSchema.description) {
3265
+ fieldDoc = ` /** ${fieldSchema.description} */\n`;
3266
+ }
3267
+ return `${fieldDoc} ${fieldName}${optionalMarker}: ${tsType};`;
3268
+ }).join('\n');
3269
+ return fields;
3270
+ }
3271
+
3272
+ // Return the processed data and generation functions
3273
+ return {
3274
+ messageSchemas,
3275
+ componentSchemas,
3276
+ messageToChannels,
3277
+ generatedTypes,
3278
+ schemaRegistry,
3279
+ // Generation functions
3280
+ generateMessageInterface,
3281
+ jsonSchemaToTypeScriptType,
3282
+ // Generate interfaces for component schemas
3283
+ generateComponentSchemas() {
3284
+ let content = '';
3285
+ componentSchemas.forEach(schema => {
3286
+ const doc = schema.description ? `/** ${schema.description} */\n` : `/** ${schema.name} */\n`;
3287
+
3288
+ // Check if this is a standalone enum schema
3289
+ if (schema.schema.type === 'string' && schema.schema.enum && Array.isArray(schema.schema.enum)) {
3290
+ // Generate union type for enum
3291
+ const enumValues = schema.schema.enum.map(val => `'${val}'`).join(' | ');
3292
+ content += `${doc}export type ${schema.typeName} = ${enumValues};\n\n`;
3293
+ } else {
3294
+ // Generate interface for object schema
3295
+ content += `${doc}export interface ${schema.typeName} {\n`;
3296
+ content += generateMessageInterface(schema.schema, schema.typeName);
3297
+ content += '\n}\n\n';
3298
+ }
3299
+
3300
+ // Track generated types to avoid duplicates
3301
+ generatedTypes.add(schema.typeName);
3302
+ });
3303
+ return content;
3304
+ },
3305
+ // Generate interfaces for each message
3306
+ generateMessageSchemas() {
3307
+ let content = '';
3308
+ messageSchemas.forEach(schema => {
3309
+ const interfaceName = `${schema.typeName}Payload`;
3310
+
3311
+ // Check if this is a duplicate of a component schema
3312
+ // For message payloads that match component schema names, skip the payload version
3313
+ if (generatedTypes.has(schema.typeName) || generatedTypes.has(interfaceName)) {
3314
+ // Skip generating the payload version if we already have the component schema
3315
+ return;
3316
+ }
3317
+ const doc = schema.description ? `/** ${schema.description} */\n` : `/** ${schema.name} message payload */\n`;
3318
+ content += `${doc}export interface ${interfaceName} {\n`;
3319
+ content += generateMessageInterface(schema.payload, schema.typeName);
3320
+ content += '\n}\n\n';
3321
+ generatedTypes.add(interfaceName);
3322
+ });
3323
+ return content;
3324
+ },
3325
+ // Generate message type constants and unions
3326
+ generateMessageTypes() {
3327
+ if (!includeMessageTypes || messageSchemas.length === 0) {
3328
+ return '';
3329
+ }
3330
+ let content = '';
3331
+
3332
+ // Generate a union type for all message payloads
3333
+ const payloadTypes = messageSchemas.map(schema => `${schema.typeName}Payload`).join(' | ');
3334
+ content += '/** Union type for all message payloads */\n';
3335
+ content += `export type MessagePayload = ${payloadTypes};\n\n`;
3336
+
3337
+ // Generate message type constants
3338
+ content += '/** Message type constants */\n';
3339
+ content += 'export const MessageTypes = {\n';
3340
+ messageSchemas.forEach(schema => {
3341
+ content += ` ${schema.typeName.toUpperCase()}: '${schema.name}',\n`;
3342
+ });
3343
+ content += '} as const;\n\n';
3344
+ content += '/** Message type union */\n';
3345
+ content += 'export type MessageType = typeof MessageTypes[keyof typeof MessageTypes];\n\n';
3346
+ return content;
3347
+ }
3348
+ };
3349
+ }
3350
+ ;// ../common/src/index.js
3351
+ /**
3352
+ * AsyncAPI Common Template Utilities
3353
+ *
3354
+ * This package provides shared utilities for AsyncAPI template generation,
3355
+ * reducing code duplication across multiple templates.
3356
+ */
3357
+
3358
+ // String utilities
3359
+
3360
+
3361
+ // Message utilities
3362
+
3363
+
3364
+ // Channel utilities
3365
+
3366
+
3367
+ // Security utilities
3368
+
3369
+
3370
+ // Template utilities
3371
+
3372
+
3373
+ // Operation utilities
3374
+
3375
+
3376
+ // Model generation utilities
3377
+
3378
+
3379
+
3380
+
3381
+ // Re-export all utilities
3382
+
3383
+
3384
+ // Version information
3385
+ const VERSION = '1.0.0';
3386
+
3387
+ // Convenience function to get all utilities in one object
3388
+ function getAllUtilities() {
3389
+ return {
3390
+ string: {
3391
+ toRustIdentifier: toRustIdentifier,
3392
+ toRustTypeName: toRustTypeName,
3393
+ toRustFieldName: toRustFieldName,
3394
+ toRustEnumVariant: toRustEnumVariant,
3395
+ toRustEnumVariantWithSerde: toRustEnumVariantWithSerde,
3396
+ toKebabCase: toKebabCase,
3397
+ toPascalCase: toPascalCase,
3398
+ toSnakeCase: toSnakeCase,
3399
+ toCamelCase: toCamelCase
3400
+ },
3401
+ message: {
3402
+ getMessageTypeName: getMessageTypeName,
3403
+ getMessageRustTypeName: getMessageRustTypeName,
3404
+ getPayloadRustTypeName: getPayloadRustTypeName,
3405
+ getMessageTypeScriptTypeName: getMessageTypeScriptTypeName,
3406
+ getPayloadTypeScriptTypeName: getPayloadTypeScriptTypeName,
3407
+ messageHasPayload: messageHasPayload,
3408
+ getMessageContentType: getMessageContentType
3409
+ },
3410
+ channel: {
3411
+ getNatsSubject: getNatsSubject,
3412
+ getChannelAddress: getChannelAddress,
3413
+ isDynamicChannel: isDynamicChannel,
3414
+ extractChannelVariables: extractChannelVariables,
3415
+ extractChannelParameters: extractChannelParameters,
3416
+ getChannelParameters: getChannelParameters,
3417
+ resolveChannelAddress: resolveChannelAddress,
3418
+ channelHasParameters: channelHasParameters,
3419
+ generateChannelParameterArgs: generateChannelParameterArgs,
3420
+ generateChannelFormatting: generateChannelFormatting,
3421
+ generateTypeScriptChannelParameterArgs: generateTypeScriptChannelParameterArgs,
3422
+ generateTypeScriptChannelFormatting: generateTypeScriptChannelFormatting,
3423
+ extractServerNameFromRef: extractServerNameFromRef,
3424
+ analyzeChannelServerMappings: analyzeChannelServerMappings,
3425
+ isChannelAllowedOnServer: isChannelAllowedOnServer
3426
+ },
3427
+ security: {
3428
+ analyzeOperationSecurity: analyzeOperationSecurity,
3429
+ operationHasSecurity: operationHasSecurity,
3430
+ operationRequiresAuth: operationRequiresAuth,
3431
+ hasSecuritySchemes: hasSecuritySchemes,
3432
+ getSecuritySchemeType: getSecuritySchemeType,
3433
+ getSecuritySchemeName: getSecuritySchemeName,
3434
+ getSecuritySchemeLocation: getSecuritySchemeLocation,
3435
+ extractOperationSecurityMap: extractOperationSecurityMap,
3436
+ getAllSecuritySchemes: getAllSecuritySchemes,
3437
+ isSecuritySchemeType: isSecuritySchemeType,
3438
+ getDefaultPort: getDefaultPort,
3439
+ validateChannelServerReferences: validateChannelServerReferences
3440
+ },
3441
+ template: {
3442
+ isTemplateVariable: isTemplateVariable,
3443
+ extractAsyncApiInfo: extractAsyncApiInfo,
3444
+ resolveTemplateParameters: resolveTemplateParameters,
3445
+ generatePackageJson: generatePackageJson,
3446
+ generateReadmeContent: generateReadmeContent,
3447
+ validateTemplateParameters: validateTemplateParameters,
3448
+ generateTypeScriptConfig: generateTypeScriptConfig,
3449
+ generateEslintConfig: generateEslintConfig,
3450
+ formatGenerationDate: formatGenerationDate,
3451
+ generateFileHeader: generateFileHeader
3452
+ },
3453
+ operation: {
3454
+ getOperationName: getOperationName,
3455
+ getOperationAction: getOperationAction,
3456
+ getOperationChannel: getOperationChannel,
3457
+ getOperationMessages: getOperationMessages,
3458
+ isOperationSend: isOperationSend,
3459
+ isOperationReceive: isOperationReceive,
3460
+ getOperationRustFunctionName: getOperationRustFunctionName,
3461
+ getOperationTypeScriptMethodName: getOperationTypeScriptMethodName,
3462
+ getOperationDescription: getOperationDescription,
3463
+ getOperationSummary: getOperationSummary,
3464
+ extractAllOperations: extractAllOperations,
3465
+ groupOperationsByAction: groupOperationsByAction,
3466
+ getOperationTraits: getOperationTraits,
3467
+ operationHasTraits: operationHasTraits,
3468
+ getOperationTags: getOperationTags,
3469
+ generateOperationHandlerName: generateOperationHandlerName
3470
+ },
3471
+ models: {
3472
+ generateRustModels: generateRustModels,
3473
+ generateMessageEnvelope: generateMessageEnvelope,
3474
+ generateTypeScriptModels: generateTypeScriptModels
3475
+ }
3476
+ };
3477
+ }
3478
+ 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 };