@ioka-technologies/asyncapi-ts-client-template 0.0.30 → 0.0.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/common/index.js +329 -150
  2. package/package.json +1 -1
@@ -2305,6 +2305,14 @@ function generateRustModels(asyncapi, options = {}) {
2305
2305
  return 'u32';
2306
2306
  case 'uint64':
2307
2307
  return 'u64';
2308
+ case 'uint16':
2309
+ return 'u16';
2310
+ case 'uint8':
2311
+ return 'u8';
2312
+ case 'int16':
2313
+ return 'i16';
2314
+ case 'int8':
2315
+ return 'i8';
2308
2316
  default:
2309
2317
  // Default to i32 for unspecified format (maintains backward compatibility)
2310
2318
  return 'i32';
@@ -2718,6 +2726,287 @@ function generateMessageEnvelope() {
2718
2726
  return `use serde::{de::DeserializeOwned, Deserialize, Serialize};
2719
2727
  use std::collections::HashMap;
2720
2728
  use uuid::Uuid;
2729
+ use chrono::{DateTime, Utc};
2730
+
2731
+ /// Correlation ID for tracing errors across operations
2732
+ #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
2733
+ pub struct CorrelationId(pub Uuid);
2734
+
2735
+ impl CorrelationId {
2736
+ pub fn new() -> Self {
2737
+ Self(Uuid::new_v4())
2738
+ }
2739
+ }
2740
+
2741
+ impl std::fmt::Display for CorrelationId {
2742
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2743
+ write!(f, "{}", self.0)
2744
+ }
2745
+ }
2746
+
2747
+ impl Default for CorrelationId {
2748
+ fn default() -> Self {
2749
+ Self::new()
2750
+ }
2751
+ }
2752
+
2753
+ /// Error severity levels for categorization and alerting
2754
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2755
+ pub enum ErrorSeverity {
2756
+ /// Low severity - informational, no action required
2757
+ Low,
2758
+ /// Medium severity - warning, monitoring required
2759
+ Medium,
2760
+ /// High severity - error, immediate attention needed
2761
+ High,
2762
+ /// Critical severity - system failure, urgent action required
2763
+ Critical,
2764
+ }
2765
+
2766
+ impl std::fmt::Display for ErrorSeverity {
2767
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2768
+ match self {
2769
+ ErrorSeverity::Low => write!(f, "LOW"),
2770
+ ErrorSeverity::Medium => write!(f, "MEDIUM"),
2771
+ ErrorSeverity::High => write!(f, "HIGH"),
2772
+ ErrorSeverity::Critical => write!(f, "CRITICAL"),
2773
+ }
2774
+ }
2775
+ }
2776
+
2777
+ /// Error category for classification and handling
2778
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2779
+ pub enum ErrorCategory {
2780
+ /// Configuration-related errors
2781
+ Configuration,
2782
+ /// Network and protocol errors
2783
+ Network,
2784
+ /// Message validation errors
2785
+ Validation,
2786
+ /// Business logic errors
2787
+ BusinessLogic,
2788
+ /// System resource errors
2789
+ Resource,
2790
+ /// Security-related errors
2791
+ Security,
2792
+ /// Serialization/deserialization errors
2793
+ Serialization,
2794
+ /// Routing errors
2795
+ Routing,
2796
+ /// Authorization errors
2797
+ Authorization,
2798
+ /// Unknown or unclassified errors
2799
+ Unknown,
2800
+ }
2801
+
2802
+ impl std::fmt::Display for ErrorCategory {
2803
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2804
+ match self {
2805
+ ErrorCategory::Configuration => write!(f, "CONFIGURATION"),
2806
+ ErrorCategory::Network => write!(f, "NETWORK"),
2807
+ ErrorCategory::Validation => write!(f, "VALIDATION"),
2808
+ ErrorCategory::BusinessLogic => write!(f, "BUSINESS_LOGIC"),
2809
+ ErrorCategory::Resource => write!(f, "RESOURCE"),
2810
+ ErrorCategory::Security => write!(f, "SECURITY"),
2811
+ ErrorCategory::Serialization => write!(f, "SERIALIZATION"),
2812
+ ErrorCategory::Routing => write!(f, "ROUTING"),
2813
+ ErrorCategory::Authorization => write!(f, "AUTHORIZATION"),
2814
+ ErrorCategory::Unknown => write!(f, "UNKNOWN"),
2815
+ }
2816
+ }
2817
+ }
2818
+
2819
+ /// Error metadata for enhanced context and monitoring
2820
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2821
+ pub struct ErrorMetadata {
2822
+ pub correlation_id: CorrelationId,
2823
+ pub severity: ErrorSeverity,
2824
+ pub category: ErrorCategory,
2825
+ pub timestamp: DateTime<Utc>,
2826
+ pub retryable: bool,
2827
+ pub kind: u32,
2828
+ #[serde(skip_serializing_if = "Option::is_none")]
2829
+ pub source_location: Option<String>,
2830
+ #[serde(skip_serializing_if = "HashMap::is_empty", default)]
2831
+ pub additional_context: HashMap<String, String>,
2832
+ }
2833
+
2834
+ impl ErrorMetadata {
2835
+ pub fn new(severity: ErrorSeverity, category: ErrorCategory, retryable: bool) -> Self {
2836
+ Self {
2837
+ correlation_id: CorrelationId::new(),
2838
+ severity,
2839
+ category,
2840
+ timestamp: Utc::now(),
2841
+ retryable,
2842
+ kind: 0,
2843
+ source_location: None,
2844
+ additional_context: HashMap::new(),
2845
+ }
2846
+ }
2847
+
2848
+ pub fn with_kind(mut self, kind: u32) -> Self {
2849
+ self.kind = kind;
2850
+ self
2851
+ }
2852
+ }
2853
+
2854
+ /// Serializable AsyncAPI error for wire transmission
2855
+ ///
2856
+ /// This error type can be sent between server and client while preserving
2857
+ /// all the rich error information needed for proper error handling.
2858
+ #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2859
+ #[serde(tag = "error_type", content = "details")]
2860
+ pub enum AsyncApiError {
2861
+ #[serde(rename = "configuration")]
2862
+ Configuration {
2863
+ message: String,
2864
+ metadata: ErrorMetadata,
2865
+ },
2866
+
2867
+ #[serde(rename = "protocol")]
2868
+ Protocol {
2869
+ message: String,
2870
+ protocol: String,
2871
+ metadata: ErrorMetadata,
2872
+ },
2873
+
2874
+ #[serde(rename = "validation")]
2875
+ Validation {
2876
+ message: String,
2877
+ #[serde(skip_serializing_if = "Option::is_none")]
2878
+ field: Option<String>,
2879
+ metadata: ErrorMetadata,
2880
+ },
2881
+
2882
+ #[serde(rename = "handler")]
2883
+ Handler {
2884
+ message: String,
2885
+ handler_name: String,
2886
+ metadata: ErrorMetadata,
2887
+ },
2888
+
2889
+ #[serde(rename = "middleware")]
2890
+ Middleware {
2891
+ message: String,
2892
+ middleware_name: String,
2893
+ metadata: ErrorMetadata,
2894
+ },
2895
+
2896
+ #[serde(rename = "recovery")]
2897
+ Recovery {
2898
+ message: String,
2899
+ attempts: u32,
2900
+ metadata: ErrorMetadata,
2901
+ },
2902
+
2903
+ #[serde(rename = "resource")]
2904
+ Resource {
2905
+ message: String,
2906
+ resource_type: String,
2907
+ metadata: ErrorMetadata,
2908
+ },
2909
+
2910
+ #[serde(rename = "security")]
2911
+ Security {
2912
+ message: String,
2913
+ metadata: ErrorMetadata,
2914
+ },
2915
+
2916
+ #[serde(rename = "authentication")]
2917
+ Authentication {
2918
+ message: String,
2919
+ auth_method: String,
2920
+ metadata: ErrorMetadata,
2921
+ },
2922
+
2923
+ #[serde(rename = "authorization")]
2924
+ Authorization {
2925
+ message: String,
2926
+ required_permissions: Vec<String>,
2927
+ metadata: ErrorMetadata,
2928
+ },
2929
+
2930
+ #[serde(rename = "rate_limit")]
2931
+ RateLimit {
2932
+ message: String,
2933
+ #[serde(skip_serializing_if = "Option::is_none")]
2934
+ retry_after_secs: Option<u64>,
2935
+ },
2936
+
2937
+ #[serde(rename = "context")]
2938
+ Context {
2939
+ message: String,
2940
+ context_key: String,
2941
+ metadata: ErrorMetadata,
2942
+ },
2943
+ }
2944
+
2945
+ impl AsyncApiError {
2946
+ /// Get error message
2947
+ pub fn message(&self) -> &str {
2948
+ match self {
2949
+ AsyncApiError::Configuration { message, .. } => message,
2950
+ AsyncApiError::Protocol { message, .. } => message,
2951
+ AsyncApiError::Validation { message, .. } => message,
2952
+ AsyncApiError::Handler { message, .. } => message,
2953
+ AsyncApiError::Middleware { message, .. } => message,
2954
+ AsyncApiError::Recovery { message, .. } => message,
2955
+ AsyncApiError::Resource { message, .. } => message,
2956
+ AsyncApiError::Security { message, .. } => message,
2957
+ AsyncApiError::Authentication { message, .. } => message,
2958
+ AsyncApiError::Authorization { message, .. } => message,
2959
+ AsyncApiError::RateLimit { message, .. } => message,
2960
+ AsyncApiError::Context { message, .. } => message,
2961
+ }
2962
+ }
2963
+
2964
+ /// Get error metadata (if available)
2965
+ pub fn metadata(&self) -> Option<&ErrorMetadata> {
2966
+ match self {
2967
+ AsyncApiError::Configuration { metadata, .. } => Some(metadata),
2968
+ AsyncApiError::Protocol { metadata, .. } => Some(metadata),
2969
+ AsyncApiError::Validation { metadata, .. } => Some(metadata),
2970
+ AsyncApiError::Handler { metadata, .. } => Some(metadata),
2971
+ AsyncApiError::Middleware { metadata, .. } => Some(metadata),
2972
+ AsyncApiError::Recovery { metadata, .. } => Some(metadata),
2973
+ AsyncApiError::Resource { metadata, .. } => Some(metadata),
2974
+ AsyncApiError::Security { metadata, .. } => Some(metadata),
2975
+ AsyncApiError::Authentication { metadata, .. } => Some(metadata),
2976
+ AsyncApiError::Authorization { metadata, .. } => Some(metadata),
2977
+ AsyncApiError::Context { metadata, .. } => Some(metadata),
2978
+ AsyncApiError::RateLimit { .. } => None,
2979
+ }
2980
+ }
2981
+
2982
+ /// Check if error is retryable
2983
+ pub fn is_retryable(&self) -> bool {
2984
+ self.metadata().map_or(false, |m| m.retryable)
2985
+ }
2986
+
2987
+ /// Get error severity
2988
+ pub fn severity(&self) -> Option<ErrorSeverity> {
2989
+ self.metadata().map(|m| m.severity)
2990
+ }
2991
+
2992
+ /// Get error category
2993
+ pub fn category(&self) -> Option<ErrorCategory> {
2994
+ self.metadata().map(|m| m.category)
2995
+ }
2996
+
2997
+ /// Get correlation ID for tracing
2998
+ pub fn correlation_id(&self) -> Option<&CorrelationId> {
2999
+ self.metadata().map(|m| &m.correlation_id)
3000
+ }
3001
+ }
3002
+
3003
+ impl std::fmt::Display for AsyncApiError {
3004
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3005
+ write!(f, "{}", self.message())
3006
+ }
3007
+ }
3008
+
3009
+ impl std::error::Error for AsyncApiError {}
2721
3010
 
2722
3011
  /// Unified message envelope for consistent AsyncAPI message format
2723
3012
  ///
@@ -2791,17 +3080,9 @@ pub struct MessageEnvelope {
2791
3080
  pub channel: Option<String>,
2792
3081
  /// Transport-level headers (auth, routing, etc.)
2793
3082
  pub headers: Option<HashMap<String, String>>,
2794
- /// Error information if applicable
2795
- pub error: Option<MessageError>,
2796
- }
2797
-
2798
- /// Error information for failed operations
2799
- #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2800
- pub struct MessageError {
2801
- /// Error code (e.g., "VALIDATION_ERROR", "TIMEOUT", "UNAUTHORIZED")
2802
- pub code: String,
2803
- /// Human-readable error message
2804
- pub message: String,
3083
+ /// Rich error information if operation failed
3084
+ /// Contains detailed error metadata including severity, category, and correlation
3085
+ pub error: Option<AsyncApiError>,
2805
3086
  }
2806
3087
 
2807
3088
  impl MessageEnvelope {
@@ -2839,11 +3120,10 @@ impl MessageEnvelope {
2839
3120
  Ok(envelope)
2840
3121
  }
2841
3122
 
2842
- /// Create an error response envelope
3123
+ /// Create an error response envelope with rich AsyncApiError
2843
3124
  pub fn error_response(
2844
3125
  operation: &str,
2845
- error_code: &str,
2846
- error_message: &str,
3126
+ error: AsyncApiError,
2847
3127
  correlation_id: Option<String>,
2848
3128
  ) -> Self {
2849
3129
  Self {
@@ -2854,13 +3134,28 @@ impl MessageEnvelope {
2854
3134
  correlation_id,
2855
3135
  channel: None,
2856
3136
  headers: None,
2857
- error: Some(MessageError {
2858
- code: error_code.to_string(),
2859
- message: error_message.to_string(),
2860
- }),
3137
+ error: Some(error),
2861
3138
  }
2862
3139
  }
2863
3140
 
3141
+ /// Create a simple error response envelope (for backward compatibility)
3142
+ pub fn simple_error_response(
3143
+ operation: &str,
3144
+ error_message: &str,
3145
+ correlation_id: Option<String>,
3146
+ ) -> Self {
3147
+ let error = AsyncApiError::Handler {
3148
+ message: error_message.to_string(),
3149
+ handler_name: operation.to_string(),
3150
+ metadata: ErrorMetadata::new(
3151
+ ErrorSeverity::High,
3152
+ ErrorCategory::BusinessLogic,
3153
+ false,
3154
+ ),
3155
+ };
3156
+ Self::error_response(operation, error, correlation_id)
3157
+ }
3158
+
2864
3159
  /// Set the correlation ID for this envelope
2865
3160
  pub fn with_correlation_id(mut self, id: String) -> Self {
2866
3161
  self.correlation_id = Some(id);
@@ -2904,11 +3199,23 @@ impl MessageEnvelope {
2904
3199
  }
2905
3200
 
2906
3201
  /// Set an error on this envelope
2907
- pub fn with_error(mut self, code: &str, message: &str) -> Self {
2908
- self.error = Some(MessageError {
2909
- code: code.to_string(),
3202
+ pub fn with_error(mut self, error: AsyncApiError) -> Self {
3203
+ self.error = Some(error);
3204
+ self
3205
+ }
3206
+
3207
+ /// Set a simple error on this envelope (for backward compatibility)
3208
+ pub fn with_simple_error(mut self, message: &str) -> Self {
3209
+ let error = AsyncApiError::Handler {
2910
3210
  message: message.to_string(),
2911
- });
3211
+ handler_name: self.operation.clone(),
3212
+ metadata: ErrorMetadata::new(
3213
+ ErrorSeverity::High,
3214
+ ErrorCategory::BusinessLogic,
3215
+ false,
3216
+ ),
3217
+ };
3218
+ self.error = Some(error);
2912
3219
  self
2913
3220
  }
2914
3221
 
@@ -2950,134 +3257,6 @@ impl MessageEnvelope {
2950
3257
  }
2951
3258
  }
2952
3259
 
2953
- #[cfg(test)]
2954
- mod tests {
2955
- use super::*;
2956
- use serde::{Deserialize, Serialize};
2957
-
2958
- #[derive(Debug, Serialize, Deserialize, PartialEq)]
2959
- struct TestPayload {
2960
- message: String,
2961
- count: u32,
2962
- }
2963
-
2964
- #[test]
2965
- fn test_envelope_creation() {
2966
- let payload = TestPayload {
2967
- message: "test".to_string(),
2968
- count: 42,
2969
- };
2970
-
2971
- let envelope = MessageEnvelope::new("test_operation", &payload).unwrap();
2972
-
2973
- assert_eq!(envelope.operation, "test_operation");
2974
- assert!(!envelope.id.is_empty());
2975
- assert!(!envelope.timestamp.is_empty());
2976
- assert_eq!(envelope.correlation_id, None);
2977
- assert_eq!(envelope.error, None);
2978
-
2979
- let extracted: TestPayload = envelope.extract_payload().unwrap();
2980
- assert_eq!(extracted, payload);
2981
- }
2982
-
2983
- #[test]
2984
- fn test_envelope_with_correlation_id() {
2985
- let payload = TestPayload {
2986
- message: "test".to_string(),
2987
- count: 42,
2988
- };
2989
-
2990
- let correlation_id = "test-correlation-id".to_string();
2991
- let envelope = MessageEnvelope::new_with_correlation_id(
2992
- "test_operation",
2993
- &payload,
2994
- correlation_id.clone(),
2995
- ).unwrap();
2996
-
2997
- assert_eq!(envelope.correlation_id, Some(correlation_id));
2998
- }
2999
-
3000
- #[test]
3001
- fn test_error_response() {
3002
- let error_envelope = MessageEnvelope::error_response(
3003
- "test_operation_response",
3004
- "TEST_ERROR",
3005
- "Test error message",
3006
- Some("correlation-123".to_string()),
3007
- );
3008
-
3009
- assert!(error_envelope.is_error());
3010
- assert_eq!(error_envelope.correlation_id, Some("correlation-123".to_string()));
3011
- if let Some(error) = &error_envelope.error {
3012
- assert_eq!(error.code, "TEST_ERROR");
3013
- assert_eq!(error.message, "Test error message");
3014
- }
3015
- }
3016
-
3017
- #[test]
3018
- fn test_envelope_serialization() {
3019
- let payload = TestPayload {
3020
- message: "test".to_string(),
3021
- count: 42,
3022
- };
3023
-
3024
- let envelope = MessageEnvelope::new("test_operation", &payload).unwrap();
3025
- let bytes = envelope.to_bytes().unwrap();
3026
- let deserialized = MessageEnvelope::from_bytes(&bytes).unwrap();
3027
-
3028
- assert_eq!(envelope.id, deserialized.id);
3029
- assert_eq!(envelope.operation, deserialized.operation);
3030
- assert_eq!(envelope.timestamp, deserialized.timestamp);
3031
- }
3032
-
3033
- #[test]
3034
- fn test_response_creation() {
3035
- let request_payload = TestPayload {
3036
- message: "request".to_string(),
3037
- count: 1,
3038
- };
3039
-
3040
- let response_payload = TestPayload {
3041
- message: "response".to_string(),
3042
- count: 2,
3043
- };
3044
-
3045
- let request = MessageEnvelope::new_with_correlation_id(
3046
- "test_request",
3047
- &request_payload,
3048
- "test-correlation".to_string(),
3049
- ).unwrap();
3050
-
3051
- let response = request.create_response("test_response", &response_payload).unwrap();
3052
-
3053
- assert_eq!(response.operation, "test_response");
3054
- assert_eq!(response.correlation_id, request.correlation_id);
3055
-
3056
- let extracted: TestPayload = response.extract_payload().unwrap();
3057
- assert_eq!(extracted, response_payload);
3058
- }
3059
-
3060
- #[test]
3061
- fn test_headers_and_auth() {
3062
- let payload = TestPayload {
3063
- message: "test".to_string(),
3064
- count: 42,
3065
- };
3066
-
3067
- let mut auth_headers = HashMap::new();
3068
- auth_headers.insert("Authorization".to_string(), "Bearer token123".to_string());
3069
-
3070
- let envelope = MessageEnvelope::new("test_operation", &payload)
3071
- .unwrap()
3072
- .with_auth_headers(auth_headers)
3073
- .with_header("Custom-Header".to_string(), "custom-value".to_string());
3074
-
3075
- assert!(envelope.headers.is_some());
3076
- let headers = envelope.headers.unwrap();
3077
- assert_eq!(headers.get("Authorization"), Some(&"Bearer token123".to_string()));
3078
- assert_eq!(headers.get("Custom-Header"), Some(&"custom-value".to_string()));
3079
- }
3080
- }
3081
3260
  `;
3082
3261
  }
3083
3262
  ;// ../common/src/models-ts.js
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ioka-technologies/asyncapi-ts-client-template",
3
- "version": "0.0.30",
3
+ "version": "0.0.32",
4
4
  "description": "TypeScript AsyncAPI client generator template compatible with rust-asyncapi patterns",
5
5
  "main": "template/index.js",
6
6
  "scripts": {