auth_framework/sdks/
javascript.rs

1//! JavaScript/TypeScript SDK enhancements for role-system integration
2//!
3//! This module provides enhanced SDK generation for JavaScript/TypeScript clients
4//! with comprehensive RBAC functionality.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Enhanced JavaScript/TypeScript SDK configuration
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct EnhancedSdkConfig {
12    /// Base API URL
13    pub base_url: String,
14    /// API version
15    pub version: String,
16    /// Enable TypeScript types generation
17    pub typescript: bool,
18    /// Include role management functions
19    pub include_rbac: bool,
20    /// Include conditional permission helpers
21    pub include_conditional_permissions: bool,
22    /// Include audit logging
23    pub include_audit: bool,
24    /// Custom client name
25    pub client_name: String,
26}
27
28impl Default for EnhancedSdkConfig {
29    fn default() -> Self {
30        Self {
31            base_url: "https://api.example.com".to_string(),
32            version: "v1".to_string(),
33            typescript: true,
34            include_rbac: true,
35            include_conditional_permissions: true,
36            include_audit: true,
37            client_name: "AuthFrameworkClient".to_string(),
38        }
39    }
40}
41
42/// JavaScript/TypeScript SDK generator
43pub struct JsSdkGenerator {
44    config: EnhancedSdkConfig,
45}
46
47impl JsSdkGenerator {
48    /// Create new SDK generator
49    pub fn new(config: EnhancedSdkConfig) -> Self {
50        Self { config }
51    }
52
53    /// Generate complete SDK
54    pub fn generate_sdk(&self) -> Result<HashMap<String, String>, Box<dyn std::error::Error>> {
55        let mut files = HashMap::new();
56
57        // Generate base client
58        files.insert("client.ts".to_string(), self.generate_base_client()?);
59
60        // Generate types
61        if self.config.typescript {
62            files.insert("types.ts".to_string(), self.generate_types()?);
63        }
64
65        // Generate RBAC module
66        if self.config.include_rbac {
67            files.insert("rbac.ts".to_string(), self.generate_rbac_module()?);
68        }
69
70        // Generate conditional permissions module
71        if self.config.include_conditional_permissions {
72            files.insert(
73                "conditional.ts".to_string(),
74                self.generate_conditional_module()?,
75            );
76        }
77
78        // Generate audit module
79        if self.config.include_audit {
80            files.insert("audit.ts".to_string(), self.generate_audit_module()?);
81        }
82
83        // Generate utilities
84        files.insert("utils.ts".to_string(), self.generate_utils()?);
85
86        // Generate main index file
87        files.insert("index.ts".to_string(), self.generate_index()?);
88
89        // Generate package.json
90        files.insert("package.json".to_string(), self.generate_package_json()?);
91
92        // Generate README
93        files.insert("README.md".to_string(), self.generate_readme()?);
94
95        Ok(files)
96    }
97
98    /// Generate base HTTP client
99    fn generate_base_client(&self) -> Result<String, Box<dyn std::error::Error>> {
100        let client_code = format!(
101            r#"
102/**
103 * Enhanced {} with RBAC Support
104 * Generated by AuthFramework SDK Generator
105 */
106
107export interface ClientConfig {{
108  baseUrl: string;
109  apiKey?: string;
110  accessToken?: string;
111  timeout?: number;
112  retryAttempts?: number;
113}}
114
115export interface ApiResponse<T> {{
116  success: boolean;
117  data?: T;
118  error?: string;
119  message?: string;
120}}
121
122export class HttpError extends Error {{
123  constructor(
124    public status: number,
125    public statusText: string,
126    public body?: any
127  ) {{
128    super(`HTTP ${{status}}: ${{statusText}}`);
129    this.name = 'HttpError';
130  }}
131}}
132
133export class {} {{
134  private baseUrl: string;
135  private headers: Record<string, string> = {{}};
136  private timeout: number;
137  private retryAttempts: number;
138
139  constructor(config: ClientConfig) {{
140    this.baseUrl = config.baseUrl.replace(/\/$/, '');
141    this.timeout = config.timeout || 30000;
142    this.retryAttempts = config.retryAttempts || 3;
143
144    if (config.apiKey) {{
145      this.headers['X-API-Key'] = config.apiKey;
146    }}
147
148    if (config.accessToken) {{
149      this.headers['Authorization'] = `Bearer ${{config.accessToken}}`;
150    }}
151
152    this.headers['Content-Type'] = 'application/json';
153  }}
154
155  /**
156   * Set authentication token
157   */
158  setAccessToken(token: string): void {{
159    this.headers['Authorization'] = `Bearer ${{token}}`;
160  }}
161
162  /**
163   * Clear authentication token
164   */
165  clearAccessToken(): void {{
166    delete this.headers['Authorization'];
167  }}
168
169  /**
170   * Set API key
171   */
172  setApiKey(apiKey: string): void {{
173    this.headers['X-API-Key'] = apiKey;
174  }}
175
176  /**
177   * Make HTTP request with retry logic
178   */
179  private async makeRequest<T>(
180    method: string,
181    path: string,
182    body?: any,
183    headers?: Record<string, string>
184  ): Promise<ApiResponse<T>> {{
185    const url = `${{this.baseUrl}}${{path}}`;
186    const requestHeaders = {{ ...this.headers, ...headers }};
187
188    let lastError: Error | null = null;
189
190    for (let attempt = 0; attempt <= this.retryAttempts; attempt++) {{
191      try {{
192        const controller = new AbortController();
193        const timeoutId = setTimeout(() => controller.abort(), this.timeout);
194
195        const response = await fetch(url, {{
196          method,
197          headers: requestHeaders,
198          body: body ? JSON.stringify(body) : undefined,
199          signal: controller.signal,
200        }});
201
202        clearTimeout(timeoutId);
203
204        const responseData = await response.json();
205
206        if (!response.ok) {{
207          throw new HttpError(response.status, response.statusText, responseData);
208        }}
209
210        return responseData as ApiResponse<T>;
211      }} catch (error) {{
212        lastError = error as Error;
213
214        // Don't retry on client errors (4xx)
215        if (error instanceof HttpError && error.status >= 400 && error.status < 500) {{
216          throw error;
217        }}
218
219        // Wait before retry (exponential backoff)
220        if (attempt < this.retryAttempts) {{
221          await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));
222        }}
223      }}
224    }}
225
226    throw lastError || new Error('Request failed after all retries');
227  }}
228
229  /**
230   * GET request
231   */
232  async get<T>(path: string, headers?: Record<string, string>): Promise<ApiResponse<T>> {{
233    return this.makeRequest<T>('GET', path, undefined, headers);
234  }}
235
236  /**
237   * POST request
238   */
239  async post<T>(path: string, body?: any, headers?: Record<string, string>): Promise<ApiResponse<T>> {{
240    return this.makeRequest<T>('POST', path, body, headers);
241  }}
242
243  /**
244   * PUT request
245   */
246  async put<T>(path: string, body?: any, headers?: Record<string, string>): Promise<ApiResponse<T>> {{
247    return this.makeRequest<T>('PUT', path, body, headers);
248  }}
249
250  /**
251   * DELETE request
252   */
253  async delete<T>(path: string, headers?: Record<string, string>): Promise<ApiResponse<T>> {{
254    return this.makeRequest<T>('DELETE', path, undefined, headers);
255  }}
256
257  /**
258   * PATCH request
259   */
260  async patch<T>(path: string, body?: any, headers?: Record<string, string>): Promise<ApiResponse<T>> {{
261    return this.makeRequest<T>('PATCH', path, body, headers);
262  }}
263}}
264"#,
265            self.config.client_name, self.config.client_name
266        );
267
268        Ok(client_code)
269    }
270
271    /// Generate TypeScript types
272    fn generate_types(&self) -> Result<String, Box<dyn std::error::Error>> {
273        let types_code = r#"
274/**
275 * TypeScript type definitions for AuthFramework RBAC
276 */
277
278export interface Role {
279  id: string;
280  name: string;
281  description?: string;
282  parent_id?: string;
283  permissions: string[];
284  created_at: string;
285  updated_at: string;
286}
287
288export interface Permission {
289  id: string;
290  action: string;
291  resource: string;
292  conditions?: Record<string, string>;
293  created_at: string;
294}
295
296export interface UserRole {
297  role_id: string;
298  role_name: string;
299  assigned_at: string;
300  assigned_by?: string;
301  expires_at?: string;
302}
303
304export interface UserRolesResponse {
305  user_id: string;
306  roles: UserRole[];
307  effective_permissions: string[];
308}
309
310export interface CreateRoleRequest {
311  name: string;
312  description?: string;
313  parent_id?: string;
314  permissions?: string[];
315}
316
317export interface UpdateRoleRequest {
318  name?: string;
319  description?: string;
320  parent_id?: string;
321}
322
323export interface AssignRoleRequest {
324  role_id: string;
325  expires_at?: string;
326  reason?: string;
327}
328
329export interface BulkAssignRequest {
330  assignments: BulkAssignment[];
331}
332
333export interface BulkAssignment {
334  user_id: string;
335  role_id: string;
336  expires_at?: string;
337}
338
339export interface ElevateRoleRequest {
340  target_role: string;
341  duration_minutes?: number;
342  justification: string;
343}
344
345export interface PermissionCheckRequest {
346  action: string;
347  resource: string;
348  context?: Record<string, string>;
349}
350
351export interface PermissionCheckResponse {
352  granted: boolean;
353  reason: string;
354  required_roles: string[];
355  missing_permissions: string[];
356}
357
358export interface AuditEntry {
359  id: string;
360  user_id?: string;
361  action: string;
362  resource?: string;
363  result: string;
364  context: Record<string, string>;
365  timestamp: string;
366}
367
368export interface AuditLogResponse {
369  entries: AuditEntry[];
370  total_count: number;
371  page: number;
372  per_page: number;
373}
374
375export interface AuditQuery {
376  user_id?: string;
377  action?: string;
378  resource?: string;
379  start_time?: string;
380  end_time?: string;
381  page?: number;
382  per_page?: number;
383}
384
385export interface ConditionalContext {
386  time_of_day?: 'business_hours' | 'after_hours' | 'weekend' | 'holiday';
387  day_type?: 'weekday' | 'weekend' | 'holiday';
388  device_type?: 'desktop' | 'mobile' | 'tablet' | 'unknown';
389  connection_type?: 'direct' | 'vpn' | 'proxy' | 'tor' | 'corporate' | 'unknown';
390  security_level?: 'low' | 'medium' | 'high' | 'critical';
391  risk_score?: number;
392  ip_address?: string;
393  user_agent?: string;
394  custom_attributes?: Record<string, string>;
395}
396
397export interface RoleListQuery {
398  page?: number;
399  per_page?: number;
400  parent_id?: string;
401  include_permissions?: boolean;
402}
403
404export type TimeOfDay = 'business_hours' | 'after_hours' | 'weekend' | 'holiday';
405export type DayType = 'weekday' | 'weekend' | 'holiday';
406export type DeviceType = 'desktop' | 'mobile' | 'tablet' | 'unknown';
407export type ConnectionType = 'direct' | 'vpn' | 'proxy' | 'tor' | 'corporate' | 'unknown';
408export type SecurityLevel = 'low' | 'medium' | 'high' | 'critical';
409"#;
410
411        Ok(types_code.to_string())
412    }
413
414    /// Generate RBAC module
415    fn generate_rbac_module(&self) -> Result<String, Box<dyn std::error::Error>> {
416        let rbac_code = format!(
417            r#"
418/**
419 * RBAC (Role-Based Access Control) Module
420 * Provides comprehensive role and permission management
421 */
422
423import {{ {} }} from './client';
424import {{
425  Role, CreateRoleRequest, UpdateRoleRequest, UserRolesResponse,
426  AssignRoleRequest, BulkAssignRequest, ElevateRoleRequest,
427  PermissionCheckRequest, PermissionCheckResponse, RoleListQuery,
428  ApiResponse
429}} from './types';
430
431export class RbacManager {{
432  constructor(private client: {}) {{}}
433
434  // ============================================================================
435  // ROLE MANAGEMENT
436  // ============================================================================
437
438  /**
439   * Create a new role
440   */
441  async createRole(request: CreateRoleRequest): Promise<ApiResponse<Role>> {{
442    return this.client.post<Role>('/{}/rbac/roles', request);
443  }}
444
445  /**
446   * Get role by ID
447   */
448  async getRole(roleId: string): Promise<ApiResponse<Role>> {{
449    return this.client.get<Role>(`/{}/rbac/roles/${{roleId}}`);
450  }}
451
452  /**
453   * List all roles with pagination
454   */
455  async listRoles(query?: RoleListQuery): Promise<ApiResponse<Role[]>> {{
456    const params = new URLSearchParams();
457    if (query?.page) params.append('page', query.page.toString());
458    if (query?.per_page) params.append('per_page', query.per_page.toString());
459    if (query?.parent_id) params.append('parent_id', query.parent_id);
460    if (query?.include_permissions) params.append('include_permissions', query.include_permissions.toString());
461
462    const queryString = params.toString();
463    const path = queryString ? `/{}/rbac/roles?${{queryString}}` : '/{}/rbac/roles';
464
465    return this.client.get<Role[]>(path);
466  }}
467
468  /**
469   * Update an existing role
470   */
471  async updateRole(roleId: string, request: UpdateRoleRequest): Promise<ApiResponse<Role>> {{
472    return this.client.put<Role>(`/{}/rbac/roles/${{roleId}}`, request);
473  }}
474
475  /**
476   * Delete a role
477   */
478  async deleteRole(roleId: string): Promise<ApiResponse<void>> {{
479    return this.client.delete<void>(`/{}/rbac/roles/${{roleId}}`);
480  }}
481
482  // ============================================================================
483  // USER ROLE ASSIGNMENTS
484  // ============================================================================
485
486  /**
487   * Assign role to user
488   */
489  async assignUserRole(userId: string, request: AssignRoleRequest): Promise<ApiResponse<void>> {{
490    return this.client.post<void>(`/{}/rbac/users/${{userId}}/roles`, request);
491  }}
492
493  /**
494   * Revoke role from user
495   */
496  async revokeUserRole(userId: string, roleId: string): Promise<ApiResponse<void>> {{
497    return this.client.delete<void>(`/{}/rbac/users/${{userId}}/roles/${{roleId}}`);
498  }}
499
500  /**
501   * Get user's roles and effective permissions
502   */
503  async getUserRoles(userId: string): Promise<ApiResponse<UserRolesResponse>> {{
504    return this.client.get<UserRolesResponse>(`/{}/rbac/users/${{userId}}/roles`);
505  }}
506
507  /**
508   * Bulk assign roles to multiple users
509   */
510  async bulkAssignRoles(request: BulkAssignRequest): Promise<ApiResponse<void>> {{
511    return this.client.post<void>('/{}/rbac/bulk/assign', request);
512  }}
513
514  // ============================================================================
515  // PERMISSION CHECKING
516  // ============================================================================
517
518  /**
519   * Check if current user has permission
520   */
521  async checkPermission(request: PermissionCheckRequest): Promise<ApiResponse<PermissionCheckResponse>> {{
522    return this.client.post<PermissionCheckResponse>('/{}/rbac/check-permission', request);
523  }}
524
525  /**
526   * Quick permission check (returns boolean)
527   */
528  async hasPermission(action: string, resource: string, context?: Record<string, string>): Promise<boolean> {{
529    try {{
530      const response = await this.checkPermission({{ action, resource, context }});
531      return response.data?.granted || false;
532    }} catch (error) {{
533      console.error('Permission check failed:', error);
534      return false;
535    }}
536  }}
537
538  /**
539   * Request role elevation
540   */
541  async elevateRole(request: ElevateRoleRequest): Promise<ApiResponse<void>> {{
542    return this.client.post<void>('/{}/rbac/elevate', request);
543  }}
544
545  // ============================================================================
546  // CONVENIENCE METHODS
547  // ============================================================================
548
549  /**
550   * Check if user has any of the specified roles
551   */
552  async userHasAnyRole(userId: string, roleNames: string[]): Promise<boolean> {{
553    try {{
554      const response = await this.getUserRoles(userId);
555      if (!response.data) return false;
556
557      const userRoleNames = response.data.roles.map(r => r.role_name);
558      return roleNames.some(role => userRoleNames.includes(role));
559    }} catch (error) {{
560      console.error('Role check failed:', error);
561      return false;
562    }}
563  }}
564
565  /**
566   * Check if user has all of the specified roles
567   */
568  async userHasAllRoles(userId: string, roleNames: string[]): Promise<boolean> {{
569    try {{
570      const response = await this.getUserRoles(userId);
571      if (!response.data) return false;
572
573      const userRoleNames = response.data.roles.map(r => r.role_name);
574      return roleNames.every(role => userRoleNames.includes(role));
575    }} catch (error) {{
576      console.error('Role check failed:', error);
577      return false;
578    }}
579  }}
580
581  /**
582   * Get role hierarchy (parent-child relationships)
583   */
584  async getRoleHierarchy(): Promise<Record<string, string[]>> {{
585    try {{
586      const response = await this.listRoles({{ include_permissions: false }});
587      if (!response.data) return {{}};
588
589      const hierarchy: Record<string, string[]> = {{}};
590
591      for (const role of response.data) {{
592        if (role.parent_id) {{
593          if (!hierarchy[role.parent_id]) {{
594            hierarchy[role.parent_id] = [];
595          }}
596          hierarchy[role.parent_id].push(role.id);
597        }}
598      }}
599
600      return hierarchy;
601    }} catch (error) {{
602      console.error('Failed to build role hierarchy:', error);
603      return {{}};
604    }}
605  }}
606
607  /**
608   * Get all child roles for a given parent role
609   */
610  async getChildRoles(parentRoleId: string): Promise<Role[]> {{
611    try {{
612      const response = await this.listRoles({{ parent_id: parentRoleId }});
613      return response.data || [];
614    }} catch (error) {{
615      console.error('Failed to get child roles:', error);
616      return [];
617    }}
618  }}
619}}
620"#,
621            self.config.client_name,
622            self.config.client_name,
623            self.config.version,
624            self.config.version,
625            self.config.version,
626            self.config.version,
627            self.config.version,
628            self.config.version,
629            self.config.version,
630            self.config.version,
631            self.config.version,
632            self.config.version,
633            self.config.version,
634            self.config.version
635        );
636
637        Ok(rbac_code)
638    }
639
640    /// Generate conditional permissions module
641    fn generate_conditional_module(&self) -> Result<String, Box<dyn std::error::Error>> {
642        let conditional_code = r#"
643/**
644 * Conditional Permissions Module
645 * Provides context-aware permission checking based on time, location, device, etc.
646 */
647
648import { ConditionalContext, PermissionCheckRequest, PermissionCheckResponse, ApiResponse } from './types';
649
650export class ConditionalPermissions {
651  constructor(private rbacManager: any) {}
652
653  /**
654   * Build context from current browser/environment
655   */
656  buildContext(): ConditionalContext {
657    const context: ConditionalContext = {};
658
659    // Time-based context
660    const now = new Date();
661    const hour = now.getHours();
662    const dayOfWeek = now.getDay(); // 0 = Sunday, 6 = Saturday
663
664    if (hour >= 9 && hour < 17 && dayOfWeek >= 1 && dayOfWeek <= 5) {
665      context.time_of_day = 'business_hours';
666      context.day_type = 'weekday';
667    } else if (dayOfWeek === 0 || dayOfWeek === 6) {
668      context.time_of_day = 'weekend';
669      context.day_type = 'weekend';
670    } else {
671      context.time_of_day = 'after_hours';
672      context.day_type = 'weekday';
673    }
674
675    // Device detection
676    if (navigator.userAgent) {
677      const ua = navigator.userAgent.toLowerCase();
678      if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) {
679        context.device_type = 'mobile';
680      } else if (ua.includes('tablet') || ua.includes('ipad')) {
681        context.device_type = 'tablet';
682      } else {
683        context.device_type = 'desktop';
684      }
685
686      context.user_agent = navigator.userAgent;
687    }
688
689    // Connection type detection (basic)
690    if (navigator.connection && (navigator.connection as any).effectiveType) {
691      const connection = navigator.connection as any;
692      if (connection.effectiveType === 'slow-2g' || connection.effectiveType === '2g') {
693        context.connection_type = 'proxy'; // Might indicate proxy/VPN
694      } else {
695        context.connection_type = 'direct';
696      }
697    }
698
699    return context;
700  }
701
702  /**
703   * Check permission with current environmental context
704   */
705  async checkPermissionWithContext(
706    action: string,
707    resource: string,
708    additionalContext?: Record<string, string>
709  ): Promise<ApiResponse<PermissionCheckResponse>> {
710    const context = this.buildContext();
711    const contextMap: Record<string, string> = {};
712
713    // Convert context to string map
714    if (context.time_of_day) contextMap.time_of_day = context.time_of_day;
715    if (context.day_type) contextMap.day_type = context.day_type;
716    if (context.device_type) contextMap.device_type = context.device_type;
717    if (context.connection_type) contextMap.connection_type = context.connection_type;
718    if (context.user_agent) contextMap.user_agent = context.user_agent;
719
720    // Add any additional context
721    if (additionalContext) {
722      Object.assign(contextMap, additionalContext);
723    }
724
725    return this.rbacManager.checkPermission({
726      action,
727      resource,
728      context: contextMap
729    });
730  }
731
732  /**
733   * Check if current time is within business hours
734   */
735  isBusinessHours(): boolean {
736    const now = new Date();
737    const hour = now.getHours();
738    const dayOfWeek = now.getDay();
739
740    return hour >= 9 && hour < 17 && dayOfWeek >= 1 && dayOfWeek <= 5;
741  }
742
743  /**
744   * Check if current day is weekend
745   */
746  isWeekend(): boolean {
747    const dayOfWeek = new Date().getDay();
748    return dayOfWeek === 0 || dayOfWeek === 6;
749  }
750
751  /**
752   * Get device type from user agent
753   */
754  getDeviceType(): 'desktop' | 'mobile' | 'tablet' | 'unknown' {
755    if (!navigator.userAgent) return 'unknown';
756
757    const ua = navigator.userAgent.toLowerCase();
758    if (ua.includes('mobile') || ua.includes('android') || ua.includes('iphone')) {
759      return 'mobile';
760    } else if (ua.includes('tablet') || ua.includes('ipad')) {
761      return 'tablet';
762    } else if (ua.includes('mozilla') || ua.includes('chrome') || ua.includes('firefox')) {
763      return 'desktop';
764    } else {
765      return 'unknown';
766    }
767  }
768
769  /**
770   * Calculate basic risk score based on context
771   */
772  calculateRiskScore(context?: ConditionalContext): number {
773    let risk = 0;
774    const ctx = context || this.buildContext();
775
776    // Time-based risk
777    if (ctx.time_of_day === 'after_hours') risk += 10;
778    if (ctx.day_type === 'weekend') risk += 5;
779
780    // Device-based risk
781    if (ctx.device_type === 'mobile') risk += 5;
782    if (ctx.device_type === 'unknown') risk += 15;
783
784    // Connection-based risk
785    if (ctx.connection_type === 'proxy') risk += 20;
786    if (ctx.connection_type === 'tor') risk += 50;
787
788    return Math.min(risk, 100); // Cap at 100
789  }
790
791  /**
792   * Create context for high-security operations
793   */
794  createHighSecurityContext(): Record<string, string> {
795    const context = this.buildContext();
796    const contextMap: Record<string, string> = {};
797
798    // Convert to string map
799    if (context.time_of_day) contextMap.time_of_day = context.time_of_day;
800    if (context.day_type) contextMap.day_type = context.day_type;
801    if (context.device_type) contextMap.device_type = context.device_type;
802    if (context.connection_type) contextMap.connection_type = context.connection_type;
803
804    // Add security requirements
805    contextMap.security_level = 'high';
806    contextMap.require_business_hours = 'true';
807    contextMap.block_vpn = 'true';
808    contextMap.max_risk_score = '20';
809
810    return contextMap;
811  }
812}
813"#;
814
815        Ok(conditional_code.to_string())
816    }
817
818    /// Generate audit module
819    fn generate_audit_module(&self) -> Result<String, Box<dyn std::error::Error>> {
820        let audit_code = format!(
821            r#"
822/**
823 * Audit Logging Module
824 * Provides comprehensive audit trail functionality
825 */
826
827import {{ {} }} from './client';
828import {{ AuditLogResponse, AuditQuery, AuditEntry, ApiResponse }} from './types';
829
830export class AuditManager {{
831  constructor(private client: {}) {{}}
832
833  /**
834   * Get audit logs with filtering and pagination
835   */
836  async getAuditLogs(query?: AuditQuery): Promise<ApiResponse<AuditLogResponse>> {{
837    const params = new URLSearchParams();
838
839    if (query?.user_id) params.append('user_id', query.user_id);
840    if (query?.action) params.append('action', query.action);
841    if (query?.resource) params.append('resource', query.resource);
842    if (query?.start_time) params.append('start_time', query.start_time);
843    if (query?.end_time) params.append('end_time', query.end_time);
844    if (query?.page) params.append('page', query.page.toString());
845    if (query?.per_page) params.append('per_page', query.per_page.toString());
846
847    const queryString = params.toString();
848    const path = queryString ? `/{}/rbac/audit?${{queryString}}` : '/{}/rbac/audit';
849
850    return this.client.get<AuditLogResponse>(path);
851  }}
852
853  /**
854   * Get audit logs for specific user
855   */
856  async getUserAuditLogs(
857    userId: string,
858    options?: {{ startTime?: string; endTime?: string; page?: number; perPage?: number }}
859  ): Promise<ApiResponse<AuditLogResponse>> {{
860    return this.getAuditLogs({{
861      user_id: userId,
862      start_time: options?.startTime,
863      end_time: options?.endTime,
864      page: options?.page,
865      per_page: options?.perPage
866    }});
867  }}
868
869  /**
870   * Get audit logs for specific action
871   */
872  async getActionAuditLogs(
873    action: string,
874    options?: {{ startTime?: string; endTime?: string; page?: number; perPage?: number }}
875  ): Promise<ApiResponse<AuditLogResponse>> {{
876    return this.getAuditLogs({{
877      action,
878      start_time: options?.startTime,
879      end_time: options?.endTime,
880      page: options?.page,
881      per_page: options?.perPage
882    }});
883  }}
884
885  /**
886   * Get audit logs for specific resource
887   */
888  async getResourceAuditLogs(
889    resource: string,
890    options?: {{ startTime?: string; endTime?: string; page?: number; perPage?: number }}
891  ): Promise<ApiResponse<AuditLogResponse>> {{
892    return this.getAuditLogs({{
893      resource,
894      start_time: options?.startTime,
895      end_time: options?.endTime,
896      page: options?.page,
897      per_page: options?.perPage
898    }});
899  }}
900
901  /**
902   * Get audit logs for the last N hours
903   */
904  async getRecentAuditLogs(hours: number = 24): Promise<ApiResponse<AuditLogResponse>> {{
905    const endTime = new Date();
906    const startTime = new Date(endTime.getTime() - (hours * 60 * 60 * 1000));
907
908    return this.getAuditLogs({{
909      start_time: startTime.toISOString(),
910      end_time: endTime.toISOString()
911    }});
912  }}
913
914  /**
915   * Export audit logs as CSV
916   */
917  async exportAuditLogs(query?: AuditQuery): Promise<string> {{
918    try {{
919      const response = await this.getAuditLogs(query);
920      if (!response.data?.entries) {{
921        return '';
922      }}
923
924      const headers = ['Timestamp', 'User ID', 'Action', 'Resource', 'Result', 'Context'];
925      const rows = [headers.join(',')];
926
927      for (const entry of response.data.entries) {{
928        const row = [
929          entry.timestamp,
930          entry.user_id || '',
931          entry.action,
932          entry.resource || '',
933          entry.result,
934          JSON.stringify(entry.context).replace(/"/g, '""') // Escape quotes for CSV
935        ];
936        rows.push(row.map(field => `"${{field}}"`).join(','));
937      }}
938
939      return rows.join('\\n');
940    }} catch (error) {{
941      console.error('Failed to export audit logs:', error);
942      throw error;
943    }}
944  }}
945
946  /**
947   * Get audit statistics
948   */
949  async getAuditStatistics(
950    startTime?: string,
951    endTime?: string
952  ): Promise<{{
953    totalEntries: number;
954    actionCounts: Record<string, number>;
955    userCounts: Record<string, number>;
956    successRate: number;
957  }}> {{
958    try {{
959      const response = await this.getAuditLogs({{
960        start_time: startTime,
961        end_time: endTime,
962        per_page: 1000 // Get a large sample
963      }});
964
965      if (!response.data?.entries) {{
966        return {{
967          totalEntries: 0,
968          actionCounts: {{}},
969          userCounts: {{}},
970          successRate: 0
971        }};
972      }}
973
974      const entries = response.data.entries;
975      const actionCounts: Record<string, number> = {{}};
976      const userCounts: Record<string, number> = {{}};
977      let successCount = 0;
978
979      for (const entry of entries) {{
980        // Count actions
981        actionCounts[entry.action] = (actionCounts[entry.action] || 0) + 1;
982
983        // Count users
984        if (entry.user_id) {{
985          userCounts[entry.user_id] = (userCounts[entry.user_id] || 0) + 1;
986        }}
987
988        // Count successes
989        if (entry.result === 'success' || entry.result === 'granted') {{
990          successCount++;
991        }}
992      }}
993
994      return {{
995        totalEntries: response.data.total_count,
996        actionCounts,
997        userCounts,
998        successRate: entries.length > 0 ? (successCount / entries.length) * 100 : 0
999      }};
1000    }} catch (error) {{
1001      console.error('Failed to get audit statistics:', error);
1002      throw error;
1003    }}
1004  }}
1005
1006  /**
1007   * Monitor real-time audit events (if supported by backend)
1008   */
1009  monitorAuditEvents(
1010    callback: (entry: AuditEntry) => void,
1011    filters?: {{ userId?: string; action?: string; resource?: string }}
1012  ): () => void {{
1013    // This would typically use WebSockets or Server-Sent Events
1014    // For now, we'll implement polling as a fallback
1015
1016    let isMonitoring = true;
1017    let lastTimestamp = new Date().toISOString();
1018
1019    const poll = async () => {{
1020      if (!isMonitoring) return;
1021
1022      try {{
1023        const response = await this.getAuditLogs({{
1024          user_id: filters?.userId,
1025          action: filters?.action,
1026          resource: filters?.resource,
1027          start_time: lastTimestamp,
1028          per_page: 50
1029        }});
1030
1031        if (response.data?.entries) {{
1032          for (const entry of response.data.entries) {{
1033            callback(entry);
1034            if (entry.timestamp > lastTimestamp) {{
1035              lastTimestamp = entry.timestamp;
1036            }}
1037          }}
1038        }}
1039      }} catch (error) {{
1040        console.error('Error polling audit events:', error);
1041      }}
1042
1043      // Poll every 5 seconds
1044      setTimeout(poll, 5000);
1045    }};
1046
1047    // Start polling
1048    poll();
1049
1050    // Return cleanup function
1051    return () => {{
1052      isMonitoring = false;
1053    }};
1054  }}
1055}}
1056"#,
1057            self.config.client_name,
1058            self.config.client_name,
1059            self.config.version,
1060            self.config.version
1061        );
1062
1063        Ok(audit_code)
1064    }
1065
1066    /// Generate utilities
1067    fn generate_utils(&self) -> Result<String, Box<dyn std::error::Error>> {
1068        let utils_code = r#"
1069/**
1070 * Utility functions for AuthFramework SDK
1071 */
1072
1073/**
1074 * Sleep for specified milliseconds
1075 */
1076export function sleep(ms: number): Promise<void> {
1077  return new Promise(resolve => setTimeout(resolve, ms));
1078}
1079
1080/**
1081 * Retry a function with exponential backoff
1082 */
1083export async function retryWithBackoff<T>(
1084  fn: () => Promise<T>,
1085  maxAttempts: number = 3,
1086  baseDelay: number = 1000
1087): Promise<T> {
1088  let lastError: Error | null = null;
1089
1090  for (let attempt = 0; attempt < maxAttempts; attempt++) {
1091    try {
1092      return await fn();
1093    } catch (error) {
1094      lastError = error as Error;
1095
1096      if (attempt < maxAttempts - 1) {
1097        const delay = baseDelay * Math.pow(2, attempt);
1098        await sleep(delay);
1099      }
1100    }
1101  }
1102
1103  throw lastError || new Error('All retry attempts failed');
1104}
1105
1106/**
1107 * Debounce function calls
1108 */
1109export function debounce<T extends (...args: any[]) => any>(
1110  func: T,
1111  wait: number
1112): (...args: Parameters<T>) => void {
1113  let timeout: NodeJS.Timeout | null = null;
1114
1115  return function(this: any, ...args: Parameters<T>) {
1116    const later = () => {
1117      timeout = null;
1118      func.apply(this, args);
1119    };
1120
1121    if (timeout) {
1122      clearTimeout(timeout);
1123    }
1124    timeout = setTimeout(later, wait);
1125  };
1126}
1127
1128/**
1129 * Throttle function calls
1130 */
1131export function throttle<T extends (...args: any[]) => any>(
1132  func: T,
1133  limit: number
1134): (...args: Parameters<T>) => void {
1135  let inThrottle: boolean;
1136
1137  return function(this: any, ...args: Parameters<T>) {
1138    if (!inThrottle) {
1139      func.apply(this, args);
1140      inThrottle = true;
1141      setTimeout(() => inThrottle = false, limit);
1142    }
1143  };
1144}
1145
1146/**
1147 * Format date for API requests
1148 */
1149export function formatDateForApi(date: Date): string {
1150  return date.toISOString();
1151}
1152
1153/**
1154 * Parse API date response
1155 */
1156export function parseApiDate(dateString: string): Date {
1157  return new Date(dateString);
1158}
1159
1160/**
1161 * Check if error is network-related
1162 */
1163export function isNetworkError(error: any): boolean {
1164  return error instanceof TypeError && error.message.includes('fetch');
1165}
1166
1167/**
1168 * Check if error is authentication-related
1169 */
1170export function isAuthError(error: any): boolean {
1171  return error.status === 401 || error.status === 403;
1172}
1173
1174/**
1175 * Check if error is rate limit-related
1176 */
1177export function isRateLimitError(error: any): boolean {
1178  return error.status === 429;
1179}
1180
1181/**
1182 * Extract error message from API response
1183 */
1184export function extractErrorMessage(error: any): string {
1185  if (error.body?.message) return error.body.message;
1186  if (error.body?.error) return error.body.error;
1187  if (error.message) return error.message;
1188  return 'Unknown error occurred';
1189}
1190
1191/**
1192 * Validate email format
1193 */
1194export function isValidEmail(email: string): boolean {
1195  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
1196  return emailRegex.test(email);
1197}
1198
1199/**
1200 * Generate UUID v4
1201 */
1202export function generateUuid(): string {
1203  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1204    const r = Math.random() * 16 | 0;
1205    const v = c === 'x' ? r : (r & 0x3 | 0x8);
1206    return v.toString(16);
1207  });
1208}
1209
1210/**
1211 * Deep merge objects
1212 */
1213export function deepMerge<T>(target: T, source: Partial<T>): T {
1214  const output = { ...target };
1215
1216  if (isObject(target) && isObject(source)) {
1217    Object.keys(source).forEach(key => {
1218      if (isObject(source[key as keyof T])) {
1219        if (!(key in target)) {
1220          Object.assign(output, { [key]: source[key as keyof T] });
1221        } else {
1222          (output as any)[key] = deepMerge(target[key as keyof T], source[key as keyof T]);
1223        }
1224      } else {
1225        Object.assign(output, { [key]: source[key as keyof T] });
1226      }
1227    });
1228  }
1229
1230  return output;
1231}
1232
1233function isObject(item: any): boolean {
1234  return item && typeof item === 'object' && !Array.isArray(item);
1235}
1236
1237/**
1238 * Local storage wrapper with error handling
1239 */
1240export class SafeStorage {
1241  static get(key: string): string | null {
1242    try {
1243      return localStorage.getItem(key);
1244    } catch {
1245      return null;
1246    }
1247  }
1248
1249  static set(key: string, value: string): boolean {
1250    try {
1251      localStorage.setItem(key, value);
1252      return true;
1253    } catch {
1254      return false;
1255    }
1256  }
1257
1258  static remove(key: string): boolean {
1259    try {
1260      localStorage.removeItem(key);
1261      return true;
1262    } catch {
1263      return false;
1264    }
1265  }
1266
1267  static clear(): boolean {
1268    try {
1269      localStorage.clear();
1270      return true;
1271    } catch {
1272      return false;
1273    }
1274  }
1275}
1276"#;
1277
1278        Ok(utils_code.to_string())
1279    }
1280
1281    /// Generate main index file
1282    fn generate_index(&self) -> Result<String, Box<dyn std::error::Error>> {
1283        let index_code = format!(
1284            r#"
1285/**
1286 * AuthFramework Enhanced SDK with RBAC Support
1287 *
1288 * @version {version}
1289 * @description Comprehensive authentication and authorization client library
1290 */
1291
1292// Core client
1293export {{ {client_name}, HttpError }} from './client';
1294export type {{ ClientConfig, ApiResponse }} from './client';
1295
1296// Type definitions
1297export * from './types';
1298
1299// RBAC module
1300{rbac_export}
1301
1302// Conditional permissions
1303{conditional_export}
1304
1305// Audit logging
1306{audit_export}
1307
1308// Utilities
1309export * from './utils';
1310
1311// Main SDK class that combines all functionality
1312export class AuthFrameworkSdk {{
1313  public readonly client: {client_name};
1314  {rbac_property}
1315  {conditional_property}
1316  {audit_property}
1317
1318  constructor(config: ClientConfig) {{
1319    this.client = new {client_name}(config);
1320    {rbac_init}
1321    {conditional_init}
1322    {audit_init}
1323  }}
1324
1325  /**
1326   * Set authentication token for all requests
1327   */
1328  setAccessToken(token: string): void {{
1329    this.client.setAccessToken(token);
1330  }}
1331
1332  /**
1333   * Clear authentication token
1334   */
1335  clearAccessToken(): void {{
1336    this.client.clearAccessToken();
1337  }}
1338
1339  /**
1340   * Set API key for authentication
1341   */
1342  setApiKey(apiKey: string): void {{
1343    this.client.setApiKey(apiKey);
1344  }}
1345
1346  /**
1347   * Check if user is authenticated
1348   */
1349  isAuthenticated(): boolean {{
1350    // This would check if we have a valid token
1351    // Implementation depends on your token storage strategy
1352    return false; // Placeholder
1353  }}
1354
1355  /**
1356   * Refresh authentication token
1357   */
1358  async refreshToken(refreshToken: string): Promise<{{ accessToken: string; refreshToken: string }}> {{
1359    // Implementation would depend on your refresh token flow
1360    throw new Error('Not implemented');
1361  }}
1362}}
1363
1364// Default export
1365export default AuthFrameworkSdk;
1366
1367// Create convenience function
1368export function createAuthFrameworkClient(config: ClientConfig): AuthFrameworkSdk {{
1369  return new AuthFrameworkSdk(config);
1370}}
1371"#,
1372            version = self.config.version,
1373            client_name = self.config.client_name,
1374            rbac_export = if self.config.include_rbac {
1375                "export { RbacManager } from './rbac';"
1376            } else {
1377                ""
1378            },
1379            conditional_export = if self.config.include_conditional_permissions {
1380                "export { ConditionalPermissions } from './conditional';"
1381            } else {
1382                ""
1383            },
1384            audit_export = if self.config.include_audit {
1385                "export { AuditManager } from './audit';"
1386            } else {
1387                ""
1388            },
1389            rbac_property = if self.config.include_rbac {
1390                "public readonly rbac: RbacManager;"
1391            } else {
1392                ""
1393            },
1394            conditional_property = if self.config.include_conditional_permissions {
1395                "public readonly conditional: ConditionalPermissions;"
1396            } else {
1397                ""
1398            },
1399            audit_property = if self.config.include_audit {
1400                "public readonly audit: AuditManager;"
1401            } else {
1402                ""
1403            },
1404            rbac_init = if self.config.include_rbac {
1405                "this.rbac = new RbacManager(this.client);"
1406            } else {
1407                ""
1408            },
1409            conditional_init = if self.config.include_conditional_permissions {
1410                "this.conditional = new ConditionalPermissions(this.rbac);"
1411            } else {
1412                ""
1413            },
1414            audit_init = if self.config.include_audit {
1415                "this.audit = new AuditManager(this.client);"
1416            } else {
1417                ""
1418            },
1419        );
1420
1421        Ok(index_code)
1422    }
1423
1424    /// Generate package.json
1425    fn generate_package_json(&self) -> Result<String, Box<dyn std::error::Error>> {
1426        let package_json = r#"{{
1427  "name": "@authframework/client",
1428  "version": "1.0.0",
1429  "description": "Enhanced AuthFramework client library with RBAC support",
1430  "main": "dist/index.js",
1431  "types": "dist/index.d.ts",
1432  "scripts": {{
1433    "build": "tsc",
1434    "build:watch": "tsc --watch",
1435    "test": "jest",
1436    "test:watch": "jest --watch",
1437    "lint": "eslint src/**/*.ts",
1438    "lint:fix": "eslint src/**/*.ts --fix",
1439    "clean": "rimraf dist",
1440    "prepublishOnly": "npm run clean && npm run build"
1441  }},
1442  "keywords": [
1443    "auth",
1444    "authentication",
1445    "authorization",
1446    "rbac",
1447    "oauth",
1448    "jwt",
1449    "typescript"
1450  ],
1451  "author": "AuthFramework",
1452  "license": "MIT",
1453  "dependencies": {{
1454    "uuid": "^9.0.0"
1455  }},
1456  "devDependencies": {{
1457    "@types/jest": "^29.0.0",
1458    "@types/node": "^18.0.0",
1459    "@types/uuid": "^9.0.0",
1460    "@typescript-eslint/eslint-plugin": "^5.0.0",
1461    "@typescript-eslint/parser": "^5.0.0",
1462    "eslint": "^8.0.0",
1463    "jest": "^29.0.0",
1464    "rimraf": "^3.0.0",
1465    "ts-jest": "^29.0.0",
1466    "typescript": "^4.9.0"
1467  }},
1468  "files": [
1469    "dist/**/*"
1470  ],
1471  "repository": {{
1472    "type": "git",
1473    "url": "https://github.com/authframework/client-js.git"
1474  }},
1475  "bugs": {{
1476    "url": "https://github.com/authframework/client-js/issues"
1477  }},
1478  "homepage": "https://github.com/authframework/client-js#readme"
1479}}
1480"#
1481        .to_string();
1482
1483        Ok(package_json)
1484    }
1485
1486    /// Generate README
1487    fn generate_readme(&self) -> Result<String, Box<dyn std::error::Error>> {
1488        let readme = r#"# AuthFramework JavaScript/TypeScript SDK
1489
1490Enhanced client library for AuthFramework with comprehensive RBAC support powered by role-system v1.0.
1491
1492## Features
1493
1494- 🔐 **Complete Authentication** - JWT, OAuth, API keys
1495- 👥 **Enterprise RBAC** - Hierarchical roles and permissions
1496- ⏰ **Conditional Permissions** - Time, location, and context-aware access control
1497- 📊 **Audit Logging** - Comprehensive activity tracking
1498- 🚀 **TypeScript Support** - Full type safety and IntelliSense
1499- 🔄 **Automatic Retries** - Built-in error handling and retry logic
1500- 📱 **Cross-Platform** - Works in browsers and Node.js
1501
1502## Installation
1503
1504```bash
1505npm install @authframework/client
1506# or
1507yarn add @authframework/client
1508```
1509
1510## Quick Start
1511
1512```typescript
1513import {{ AuthFrameworkSdk }} from '@authframework/client';
1514
1515// Initialize the client
1516const auth = new AuthFrameworkSdk({{
1517  baseUrl: 'https://your-api.example.com',
1518  accessToken: 'your-jwt-token'
1519}});
1520
1521// Check permissions
1522const canEdit = await auth.rbac.hasPermission('edit', 'documents');
1523
1524// Get user roles
1525const userRoles = await auth.rbac.getUserRoles('user123');
1526
1527// Conditional permission check
1528const canAccessAfterHours = await auth.conditional.checkPermissionWithContext(
1529  'access',
1530  'admin-panel'
1531);
1532```
1533
1534## RBAC (Role-Based Access Control)
1535
1536### Role Management
1537
1538```typescript
1539// Create a new role
1540await auth.rbac.createRole({{
1541  name: 'Editor',
1542  description: 'Can edit content',
1543  permissions: ['edit:documents', 'read:documents']
1544}});
1545
1546// Assign role to user
1547await auth.rbac.assignUserRole('user123', {{
1548  role_id: 'editor-role-id',
1549  expires_at: '2024-12-31T23:59:59Z'
1550}});
1551
1552// Check user permissions
1553const permissions = await auth.rbac.getUserRoles('user123');
1554console.log(permissions.effective_permissions);
1555```
1556
1557### Permission Checking
1558
1559```typescript
1560// Simple permission check
1561const hasPermission = await auth.rbac.hasPermission('delete', 'documents');
1562
1563// Detailed permission check with context
1564const result = await auth.rbac.checkPermission({{
1565  action: 'access',
1566  resource: 'admin-panel',
1567  context: {{
1568    ip_address: '192.168.1.100',
1569    time_of_day: 'business_hours'
1570  }}
1571}});
1572
1573console.log(result.data?.granted); // boolean
1574console.log(result.data?.reason);  // explanation
1575```
1576
1577## Conditional Permissions
1578
1579Context-aware permissions based on time, location, device, and custom attributes.
1580
1581```typescript
1582// Check permission with environmental context
1583const canAccess = await auth.conditional.checkPermissionWithContext(
1584  'access',
1585  'sensitive-data'
1586);
1587
1588// Build custom context
1589const context = auth.conditional.buildContext();
1590console.log(context.time_of_day);    // 'business_hours' | 'after_hours' | 'weekend'
1591console.log(context.device_type);    // 'desktop' | 'mobile' | 'tablet'
1592console.log(context.connection_type); // 'direct' | 'vpn' | 'proxy'
1593
1594// High-security context for sensitive operations
1595const highSecContext = auth.conditional.createHighSecurityContext();
1596const result = await auth.rbac.checkPermission({{
1597  action: 'delete',
1598  resource: 'user-accounts',
1599  context: highSecContext
1600}});
1601```
1602
1603## Audit Logging
1604
1605Comprehensive activity tracking and analysis.
1606
1607```typescript
1608// Get audit logs
1609const logs = await auth.audit.getAuditLogs({{
1610  user_id: 'user123',
1611  start_time: '2024-01-01T00:00:00Z',
1612  end_time: '2024-01-31T23:59:59Z',
1613  page: 1,
1614  per_page: 50
1615}});
1616
1617// Get recent activity
1618const recent = await auth.audit.getRecentAuditLogs(24); // Last 24 hours
1619
1620// Export audit data
1621const csvData = await auth.audit.exportAuditLogs({{
1622  action: 'login',
1623  start_time: '2024-01-01T00:00:00Z'
1624}});
1625
1626// Real-time monitoring
1627const stopMonitoring = auth.audit.monitorAuditEvents(
1628  (entry) => console.log('New audit entry:', entry),
1629  {{ action: 'permission_check' }}
1630);
1631
1632// Stop monitoring when done
1633stopMonitoring();
1634```
1635
1636## Role Elevation
1637
1638Temporary privilege escalation for administrative tasks.
1639
1640```typescript
1641// Request elevated permissions
1642await auth.rbac.elevateRole({{
1643  target_role: 'admin',
1644  duration_minutes: 30,
1645  justification: 'Emergency system maintenance'
1646}});
1647
1648// Check if user has elevated permissions
1649const hasElevated = await auth.rbac.hasPermission('elevated', 'admin');
1650```
1651
1652## Advanced Usage
1653
1654### Bulk Operations
1655
1656```typescript
1657// Bulk role assignment
1658await auth.rbac.bulkAssignRoles({{
1659  assignments: [
1660    {{ user_id: 'user1', role_id: 'editor' }},
1661    {{ user_id: 'user2', role_id: 'viewer' }},
1662    {{ user_id: 'user3', role_id: 'admin', expires_at: '2024-12-31T23:59:59Z' }}
1663  ]
1664}});
1665```
1666
1667### Error Handling
1668
1669```typescript
1670import {{ HttpError, isAuthError, isRateLimitError }} from '@authframework/client';
1671
1672try {{
1673  await auth.rbac.createRole(roleData);
1674}} catch (error) {{
1675  if (error instanceof HttpError) {{
1676    if (isAuthError(error)) {{
1677      // Handle authentication error
1678      console.log('Authentication required');
1679    }} else if (isRateLimitError(error)) {{
1680      // Handle rate limit
1681      console.log('Rate limit exceeded');
1682    }} else {{
1683      console.log('HTTP error:', error.status, error.statusText);
1684    }}
1685  }}
1686}}
1687```
1688
1689### Custom Configuration
1690
1691```typescript
1692const auth = new AuthFrameworkSdk({{
1693  baseUrl: 'https://api.example.com',
1694  accessToken: 'your-token',
1695  timeout: 30000,        // 30 second timeout
1696  retryAttempts: 3,      // Retry failed requests 3 times
1697}});
1698
1699// Dynamic token updates
1700auth.setAccessToken('new-token');
1701
1702// API key authentication
1703auth.setApiKey('your-api-key');
1704```
1705
1706## Type Safety
1707
1708Full TypeScript support with comprehensive type definitions:
1709
1710```typescript
1711import type {{
1712  Role,
1713  Permission,
1714  UserRolesResponse,
1715  PermissionCheckResponse,
1716  ConditionalContext
1717}} from '@authframework/client';
1718
1719const role: Role = {{
1720  id: 'role-123',
1721  name: 'Editor',
1722  description: 'Content editor role',
1723  parent_id: 'base-user',
1724  permissions: ['edit:content', 'read:content'],
1725  created_at: '2024-01-01T00:00:00Z',
1726  updated_at: '2024-01-01T00:00:00Z'
1727}};
1728```
1729
1730## Browser Support
1731
1732- Chrome 60+
1733- Firefox 60+
1734- Safari 12+
1735- Edge 79+
1736
1737## Node.js Support
1738
1739- Node.js 14+
1740
1741## License
1742
1743MIT License - see LICENSE file for details.
1744
1745## Support
1746
1747- 📖 [Documentation](https://docs.authframework.com)
1748- 🐛 [Issue Tracker](https://github.com/authframework/client-js/issues)
1749- 💬 [Discord Community](https://discord.gg/authframework)
1750"#.to_string();
1751
1752        Ok(readme)
1753    }
1754}
1755
1756#[cfg(test)]
1757mod tests {
1758    use super::*;
1759
1760    #[test]
1761    fn test_sdk_generation() {
1762        let config = EnhancedSdkConfig::default();
1763        let generator = JsSdkGenerator::new(config);
1764
1765        let result = generator.generate_sdk();
1766        assert!(result.is_ok());
1767
1768        let files = result.unwrap();
1769        assert!(files.contains_key("client.ts"));
1770        assert!(files.contains_key("types.ts"));
1771        assert!(files.contains_key("rbac.ts"));
1772        assert!(files.contains_key("index.ts"));
1773        assert!(files.contains_key("package.json"));
1774    }
1775
1776    #[test]
1777    fn test_typescript_generation() {
1778        let config = EnhancedSdkConfig {
1779            typescript: true,
1780            include_rbac: true,
1781            include_conditional_permissions: true,
1782            include_audit: true,
1783            ..Default::default()
1784        };
1785
1786        let generator = JsSdkGenerator::new(config);
1787        let files = generator.generate_sdk().unwrap();
1788
1789        // Verify all modules are included
1790        assert!(files.contains_key("types.ts"));
1791        assert!(files.contains_key("rbac.ts"));
1792        assert!(files.contains_key("conditional.ts"));
1793        assert!(files.contains_key("audit.ts"));
1794    }
1795}
1796
1797