@djangocfg/ui-nextjs 1.4.45

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 (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +152 -0
  3. package/package.json +110 -0
  4. package/src/animations/AnimatedBackground.tsx +645 -0
  5. package/src/animations/index.ts +2 -0
  6. package/src/blocks/ArticleCard.tsx +94 -0
  7. package/src/blocks/ArticleList.tsx +95 -0
  8. package/src/blocks/CTASection.tsx +136 -0
  9. package/src/blocks/FeatureSection.tsx +104 -0
  10. package/src/blocks/Hero.tsx +102 -0
  11. package/src/blocks/NewsletterSection.tsx +119 -0
  12. package/src/blocks/StatsSection.tsx +103 -0
  13. package/src/blocks/SuperHero.tsx +328 -0
  14. package/src/blocks/TestimonialSection.tsx +122 -0
  15. package/src/blocks/index.ts +9 -0
  16. package/src/components/README.md +2018 -0
  17. package/src/components/breadcrumb-navigation.tsx +127 -0
  18. package/src/components/breadcrumb.tsx +132 -0
  19. package/src/components/button-download.tsx +275 -0
  20. package/src/components/dropdown-menu.tsx +219 -0
  21. package/src/components/index.ts +86 -0
  22. package/src/components/markdown/MarkdownMessage.tsx +338 -0
  23. package/src/components/markdown/index.ts +5 -0
  24. package/src/components/menubar.tsx +274 -0
  25. package/src/components/multi-select-pro/async.tsx +608 -0
  26. package/src/components/multi-select-pro/helpers.tsx +84 -0
  27. package/src/components/multi-select-pro/index.tsx +622 -0
  28. package/src/components/navigation-menu.tsx +153 -0
  29. package/src/components/pagination-static.tsx +348 -0
  30. package/src/components/pagination.tsx +138 -0
  31. package/src/components/phone-input.tsx +276 -0
  32. package/src/components/sidebar.tsx +866 -0
  33. package/src/components/sonner.tsx +31 -0
  34. package/src/components/ssr-pagination.tsx +237 -0
  35. package/src/hooks/index.ts +19 -0
  36. package/src/hooks/useCfgRouter.ts +153 -0
  37. package/src/hooks/useLocalStorage.ts +221 -0
  38. package/src/hooks/useQueryParams.ts +73 -0
  39. package/src/hooks/useSessionStorage.ts +188 -0
  40. package/src/hooks/useTheme.ts +57 -0
  41. package/src/index.ts +24 -0
  42. package/src/lib/index.ts +2 -0
  43. package/src/styles/index.css +2 -0
  44. package/src/theme/ForceTheme.tsx +115 -0
  45. package/src/theme/ThemeProvider.tsx +82 -0
  46. package/src/theme/ThemeToggle.tsx +52 -0
  47. package/src/theme/index.ts +3 -0
  48. package/src/tools/JsonForm/JsonSchemaForm.tsx +199 -0
  49. package/src/tools/JsonForm/examples/BotConfigExample.tsx +245 -0
  50. package/src/tools/JsonForm/examples/RealBotConfigExample.tsx +157 -0
  51. package/src/tools/JsonForm/index.ts +46 -0
  52. package/src/tools/JsonForm/templates/ArrayFieldItemTemplate.tsx +46 -0
  53. package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +73 -0
  54. package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +106 -0
  55. package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +34 -0
  56. package/src/tools/JsonForm/templates/FieldTemplate.tsx +61 -0
  57. package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +43 -0
  58. package/src/tools/JsonForm/templates/index.ts +12 -0
  59. package/src/tools/JsonForm/types.ts +83 -0
  60. package/src/tools/JsonForm/utils.ts +212 -0
  61. package/src/tools/JsonForm/widgets/CheckboxWidget.tsx +36 -0
  62. package/src/tools/JsonForm/widgets/NumberWidget.tsx +88 -0
  63. package/src/tools/JsonForm/widgets/SelectWidget.tsx +100 -0
  64. package/src/tools/JsonForm/widgets/SwitchWidget.tsx +34 -0
  65. package/src/tools/JsonForm/widgets/TextWidget.tsx +95 -0
  66. package/src/tools/JsonForm/widgets/index.ts +12 -0
  67. package/src/tools/JsonTree/index.tsx +252 -0
  68. package/src/tools/LottiePlayer/LottiePlayer.client.tsx +212 -0
  69. package/src/tools/LottiePlayer/index.tsx +54 -0
  70. package/src/tools/LottiePlayer/types.ts +108 -0
  71. package/src/tools/LottiePlayer/useLottie.ts +163 -0
  72. package/src/tools/Mermaid/Mermaid.client.tsx +341 -0
  73. package/src/tools/Mermaid/index.tsx +40 -0
  74. package/src/tools/OpenapiViewer/components/EndpointInfo.tsx +144 -0
  75. package/src/tools/OpenapiViewer/components/EndpointsLibrary.tsx +255 -0
  76. package/src/tools/OpenapiViewer/components/PlaygroundLayout.tsx +123 -0
  77. package/src/tools/OpenapiViewer/components/PlaygroundStepper.tsx +98 -0
  78. package/src/tools/OpenapiViewer/components/RequestBuilder.tsx +164 -0
  79. package/src/tools/OpenapiViewer/components/RequestParametersForm.tsx +253 -0
  80. package/src/tools/OpenapiViewer/components/ResponseViewer.tsx +169 -0
  81. package/src/tools/OpenapiViewer/components/VersionSelector.tsx +64 -0
  82. package/src/tools/OpenapiViewer/components/index.ts +14 -0
  83. package/src/tools/OpenapiViewer/constants.ts +39 -0
  84. package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +338 -0
  85. package/src/tools/OpenapiViewer/hooks/index.ts +8 -0
  86. package/src/tools/OpenapiViewer/hooks/useMobile.ts +10 -0
  87. package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +203 -0
  88. package/src/tools/OpenapiViewer/index.tsx +36 -0
  89. package/src/tools/OpenapiViewer/types.ts +152 -0
  90. package/src/tools/OpenapiViewer/utils/apiKeyManager.ts +149 -0
  91. package/src/tools/OpenapiViewer/utils/formatters.ts +71 -0
  92. package/src/tools/OpenapiViewer/utils/index.ts +9 -0
  93. package/src/tools/OpenapiViewer/utils/versionManager.ts +161 -0
  94. package/src/tools/PrettyCode/PrettyCode.client.tsx +217 -0
  95. package/src/tools/PrettyCode/index.tsx +43 -0
  96. package/src/tools/VideoPlayer/README.md +239 -0
  97. package/src/tools/VideoPlayer/VideoControls.tsx +138 -0
  98. package/src/tools/VideoPlayer/VideoPlayer.tsx +230 -0
  99. package/src/tools/VideoPlayer/index.ts +9 -0
  100. package/src/tools/VideoPlayer/types.ts +62 -0
  101. package/src/tools/index.ts +43 -0
@@ -0,0 +1,152 @@
1
+ // API Key type for playground
2
+ export interface ApiKey {
3
+ id: string;
4
+ name: string;
5
+ created_at?: string;
6
+ last_used_at?: string | null;
7
+ }
8
+
9
+ // OpenAPI Schema types
10
+ export interface ApiEndpoint {
11
+ name: string;
12
+ method: string;
13
+ path: string;
14
+ description: string;
15
+ category: string;
16
+ parameters?: Array<{
17
+ name: string;
18
+ type: string;
19
+ required: boolean;
20
+ description?: string;
21
+ }>;
22
+ requestBody?: {
23
+ type: string;
24
+ description?: string;
25
+ };
26
+ responses?: Array<{
27
+ code: string;
28
+ description: string;
29
+ }>;
30
+ }
31
+
32
+ export interface OpenApiSchema {
33
+ openapi: string;
34
+ info: {
35
+ title: string;
36
+ version: string;
37
+ description?: string;
38
+ };
39
+ servers?: Array<{
40
+ url: string;
41
+ description?: string;
42
+ }>;
43
+ paths: Record<string, Record<string, any>>;
44
+ components?: {
45
+ schemas?: Record<string, any>;
46
+ };
47
+ }
48
+
49
+ // Schema source configuration (URL-based only)
50
+ export interface SchemaSource {
51
+ id: string;
52
+ name: string;
53
+ url: string; // URL to fetch OpenAPI schema from (full URL)
54
+ }
55
+
56
+ // Playground configuration
57
+ export interface PlaygroundConfig {
58
+ schemas: SchemaSource[]; // Array of schema URLs (full URLs from API)
59
+ defaultSchemaId?: string; // Default schema to select
60
+ }
61
+
62
+ // Playground state types
63
+ export interface PlaygroundState {
64
+ // Step management
65
+ currentStep: PlaygroundStep;
66
+ steps: PlaygroundStep[];
67
+
68
+ // Endpoint selection
69
+ selectedEndpoint: ApiEndpoint | null;
70
+ selectedCategory: string;
71
+ searchTerm: string;
72
+ selectedVersion: string; // API version selection
73
+
74
+ // Request configuration
75
+ requestUrl: string;
76
+ requestMethod: string;
77
+ requestHeaders: string;
78
+ requestBody: string;
79
+ selectedApiKey: string | null;
80
+ manualApiToken: string; // Manual Bearer token input
81
+ parameters: Record<string, string>; // Form parameters for URL substitution
82
+
83
+ // Response
84
+ response: ApiResponse | null;
85
+ loading: boolean;
86
+
87
+ // UI state
88
+ sidebarOpen: boolean;
89
+ }
90
+
91
+ export type PlaygroundStep = 'endpoints' | 'request' | 'response';
92
+
93
+ export interface ApiResponse {
94
+ status?: number;
95
+ statusText?: string;
96
+ headers?: any;
97
+ data?: any;
98
+ error?: string;
99
+ }
100
+
101
+ // Context types
102
+ export interface PlaygroundContextType {
103
+ // State
104
+ state: PlaygroundState;
105
+ config: PlaygroundConfig;
106
+ apiKeys: ApiKey[];
107
+ apiKeysLoading: boolean;
108
+
109
+ // Step management
110
+ setCurrentStep: (step: PlaygroundStep) => void;
111
+ goToNextStep: () => void;
112
+ goToPreviousStep: () => void;
113
+
114
+ // Endpoint management
115
+ setSelectedEndpoint: (endpoint: ApiEndpoint | null) => void;
116
+ setSelectedCategory: (category: string) => void;
117
+ setSearchTerm: (term: string) => void;
118
+ setSelectedVersion: (version: string) => void;
119
+
120
+ // Request management
121
+ setRequestUrl: (url: string) => void;
122
+ setRequestMethod: (method: string) => void;
123
+ setRequestHeaders: (headers: string) => void;
124
+ setRequestBody: (body: string) => void;
125
+ setSelectedApiKey: (apiKeyId: string | null) => void;
126
+ setManualApiToken: (token: string) => void;
127
+ setParameters: (parameters: Record<string, string>) => void;
128
+
129
+ // Response management
130
+ setResponse: (response: ApiResponse | null) => void;
131
+ setLoading: (loading: boolean) => void;
132
+
133
+ // UI management
134
+ setSidebarOpen: (open: boolean) => void;
135
+
136
+ // Actions
137
+ clearAll: () => void;
138
+ copyToClipboard: (text: string) => void;
139
+ sendRequest: () => Promise<void>;
140
+ }
141
+
142
+ // Hook return types
143
+ export interface UseOpenApiSchemaReturn {
144
+ loading: boolean;
145
+ error: string | null;
146
+ endpoints: ApiEndpoint[];
147
+ categories: string[];
148
+ schemas: SchemaSource[];
149
+ currentSchema: SchemaSource | null;
150
+ setCurrentSchema: (schemaId: string) => void;
151
+ refresh: () => void;
152
+ }
@@ -0,0 +1,149 @@
1
+ /**
2
+ * API Key Management Utility
3
+ *
4
+ * Provides centralized functions for managing API keys in headers and requests.
5
+ * This utility can be used across different components that need to handle API keys.
6
+ */
7
+
8
+ import type { ApiKey } from '../types';
9
+
10
+ export type { ApiKey };
11
+
12
+ export interface HeadersWithApiKey {
13
+ [key: string]: string;
14
+ }
15
+
16
+ /**
17
+ * Add API key to request headers
18
+ * @param headers - Existing headers object
19
+ * @param apiKey - API key object or string
20
+ * @returns Headers with API key added
21
+ */
22
+ export function addApiKeyToHeaders(headers: HeadersWithApiKey, apiKey: ApiKey | string | null): HeadersWithApiKey {
23
+ if (!apiKey) {
24
+ return headers;
25
+ }
26
+
27
+ const keyValue = typeof apiKey === 'string' ? apiKey : (apiKey.id || '');
28
+
29
+ return {
30
+ ...headers,
31
+ 'X-API-Key': keyValue,
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Remove API key from headers object
37
+ * @param headers - Headers object
38
+ * @returns Headers without API key
39
+ */
40
+ export function removeApiKeyFromHeaders(headers: HeadersWithApiKey): HeadersWithApiKey {
41
+ const { 'X-API-Key': removed, ...remainingHeaders } = headers;
42
+ return remainingHeaders;
43
+ }
44
+
45
+ /**
46
+ * Remove API key from headers JSON string
47
+ * @param headersJson - JSON string of headers
48
+ * @returns JSON string without API key
49
+ */
50
+ export function removeApiKeyFromHeadersJson(headersJson: string): string {
51
+ try {
52
+ const headers = JSON.parse(headersJson);
53
+ const updatedHeaders = removeApiKeyFromHeaders(headers);
54
+ return JSON.stringify(updatedHeaders, null, 2);
55
+ } catch (error) {
56
+ console.error('Error parsing headers JSON:', error);
57
+ return headersJson;
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Update headers JSON string with API key
63
+ * @param headersJson - JSON string of headers
64
+ * @param apiKey - API key object or string
65
+ * @returns Updated JSON string
66
+ */
67
+ export function updateHeadersJsonWithApiKey(headersJson: string, apiKey: ApiKey | string | null): string {
68
+ try {
69
+ const headers = JSON.parse(headersJson);
70
+ const updatedHeaders = addApiKeyToHeaders(headers, apiKey);
71
+ return JSON.stringify(updatedHeaders, null, 2);
72
+ } catch (error) {
73
+ console.error('Error parsing headers JSON:', error);
74
+ return headersJson;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Find API key by ID in a list of API keys
80
+ * @param apiKeys - Array of API keys
81
+ * @param keyId - ID of the API key to find
82
+ * @returns API key object or null
83
+ */
84
+ export function findApiKeyById(apiKeys: ApiKey[], keyId: string): ApiKey | null {
85
+ return apiKeys.find((key) => key.id === keyId) || null;
86
+ }
87
+
88
+ /**
89
+ * Validate API key format
90
+ * @param apiKey - API key string to validate
91
+ * @returns Whether the API key format is valid
92
+ */
93
+ export function validateApiKeyFormat(apiKey: string): boolean {
94
+ // Basic validation - adjust based on your API key format requirements
95
+ return typeof apiKey === 'string' && apiKey.length > 0 && apiKey.trim() !== '';
96
+ }
97
+
98
+ /**
99
+ * Create default headers with API key
100
+ * @param apiKey - API key object or string
101
+ * @returns Default headers with API key
102
+ */
103
+ export function createDefaultHeaders(apiKey?: ApiKey | string): HeadersWithApiKey {
104
+ const defaultHeaders: HeadersWithApiKey = {
105
+ 'Content-Type': 'application/json',
106
+ };
107
+
108
+ if (apiKey) {
109
+ return addApiKeyToHeaders(defaultHeaders, apiKey);
110
+ }
111
+
112
+ return defaultHeaders;
113
+ }
114
+
115
+ /**
116
+ * Merge headers with API key
117
+ * @param baseHeaders - Base headers object
118
+ * @param additionalHeaders - Additional headers to merge
119
+ * @param apiKey - API key to add
120
+ * @returns Merged headers with API key
121
+ */
122
+ export function mergeHeadersWithApiKey(baseHeaders: HeadersWithApiKey, additionalHeaders: HeadersWithApiKey, apiKey?: ApiKey | string): HeadersWithApiKey {
123
+ const mergedHeaders = { ...baseHeaders, ...additionalHeaders };
124
+
125
+ if (apiKey) {
126
+ return addApiKeyToHeaders(mergedHeaders, apiKey);
127
+ }
128
+
129
+ return mergedHeaders;
130
+ }
131
+
132
+ /**
133
+ * Log API key usage (for debugging/analytics)
134
+ * @param apiKey - API key that was used
135
+ * @param endpoint - Endpoint that was called
136
+ * @param success - Whether the request was successful
137
+ */
138
+ export function logApiKeyUsage(apiKey: ApiKey | string, endpoint: string, success: boolean): void {
139
+ const keyValue = typeof apiKey === 'string' ? apiKey : (apiKey.id || '');
140
+ const keyName = typeof apiKey === 'string' ? 'Unknown' : apiKey.name;
141
+
142
+ console.log(`API Key Usage:`, {
143
+ keyName,
144
+ keyValue: keyValue.substring(0, 8) + '...',
145
+ endpoint,
146
+ success,
147
+ timestamp: new Date().toISOString(),
148
+ });
149
+ }
@@ -0,0 +1,71 @@
1
+ import type { ApiKey } from '../types';
2
+ import { HTTP_METHOD_COLORS, HTTP_STATUS_COLORS } from '../constants';
3
+
4
+ export const getMethodColor = (
5
+ method: string
6
+ ): 'success' | 'primary' | 'warning' | 'error' | 'default' => {
7
+ return HTTP_METHOD_COLORS[method.toUpperCase() as keyof typeof HTTP_METHOD_COLORS] || 'default';
8
+ };
9
+
10
+ export const getStatusColor = (
11
+ status: number
12
+ ): 'success' | 'warning' | 'error' | 'default' => {
13
+ const firstDigit = Math.floor(status / 100).toString();
14
+ return HTTP_STATUS_COLORS[firstDigit as keyof typeof HTTP_STATUS_COLORS] || 'default';
15
+ };
16
+
17
+ export const formatApiKeyDisplay = (apiKey: ApiKey): string => {
18
+ const preview = apiKey.id.substring(0, 8);
19
+ return `${apiKey.name} (${preview}...)`;
20
+ };
21
+
22
+ export const isValidJson = (str: string): boolean => {
23
+ if (!str || typeof str !== 'string') return false;
24
+ try {
25
+ JSON.parse(str);
26
+ return true;
27
+ } catch {
28
+ return false;
29
+ }
30
+ };
31
+
32
+ export const formatRequestHeaders = (headers: Record<string, string>): string => {
33
+ return JSON.stringify(headers, null, 2);
34
+ };
35
+
36
+ export const parseRequestHeaders = (headersString: string): Record<string, string> => {
37
+ if (!headersString || typeof headersString !== 'string') {
38
+ return { 'Content-Type': 'application/json' };
39
+ }
40
+
41
+ try {
42
+ const parsed = JSON.parse(headersString);
43
+ return typeof parsed === 'object' && parsed !== null ? parsed : { 'Content-Type': 'application/json' };
44
+ } catch {
45
+ return { 'Content-Type': 'application/json' };
46
+ }
47
+ };
48
+
49
+ // Substitute URL parameters like {id}, {userId}, etc.
50
+ export const substituteUrlParameters = (
51
+ url: string,
52
+ parameters: Record<string, string>
53
+ ): string => {
54
+ let substitutedUrl = url;
55
+
56
+ Object.entries(parameters).forEach(([key, value]) => {
57
+ if (value && value.trim() !== '') {
58
+ // Replace both {key} and %7Bkey%7D patterns (URL encoded version)
59
+ const patterns = [
60
+ new RegExp(`\\{${key}\\}`, 'g'),
61
+ new RegExp(`%7B${key}%7D`, 'gi'),
62
+ ];
63
+
64
+ patterns.forEach((pattern) => {
65
+ substitutedUrl = substitutedUrl.replace(pattern, encodeURIComponent(value));
66
+ });
67
+ }
68
+ });
69
+
70
+ return substitutedUrl;
71
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Playground Utilities
3
+ *
4
+ * Centralized exports for all playground utilities
5
+ */
6
+
7
+ export * from './apiKeyManager';
8
+ export * from './versionManager';
9
+ export * from './formatters';
@@ -0,0 +1,161 @@
1
+ /**
2
+ * API Version Management Utilities
3
+ * Handles version detection, filtering, and URL transformation
4
+ */
5
+
6
+ import type { ApiEndpoint } from '../types';
7
+
8
+ export interface ApiVersion {
9
+ id: string;
10
+ name: string;
11
+ description: string;
12
+ isDefault: boolean;
13
+ }
14
+
15
+ // Available API versions
16
+ export const API_VERSIONS: ApiVersion[] = [
17
+ {
18
+ id: 'v1',
19
+ name: 'v1',
20
+ description: 'Current stable version',
21
+ isDefault: true,
22
+ },
23
+ ];
24
+
25
+ /**
26
+ * Detect version from endpoint path
27
+ */
28
+ export const detectEndpointVersion = (path: string): string => {
29
+ // Check for versioned paths like /api/vehicles_api/v1/...
30
+ const versionMatch = path.match(/\/api\/[^/]+\/(v\d+)\//);
31
+ if (versionMatch && versionMatch[1]) {
32
+ return versionMatch[1];
33
+ }
34
+
35
+ // If no version found, default to v1
36
+ return 'v1';
37
+ };
38
+
39
+ /**
40
+ * Check if endpoint belongs to specific version
41
+ */
42
+ export const isEndpointInVersion = (endpoint: ApiEndpoint, version: string): boolean => {
43
+ const endpointVersion = detectEndpointVersion(endpoint.path);
44
+ return endpointVersion === version;
45
+ };
46
+
47
+ /**
48
+ * Filter endpoints by version
49
+ */
50
+ export const filterEndpointsByVersion = (endpoints: ApiEndpoint[], version: string): ApiEndpoint[] => {
51
+ return endpoints.filter(endpoint => isEndpointInVersion(endpoint, version));
52
+ };
53
+
54
+ /**
55
+ * Remove duplicate endpoints across versions
56
+ * Keeps only the specified version, removes duplicates from other versions
57
+ */
58
+ export const deduplicateEndpoints = (endpoints: ApiEndpoint[], preferredVersion: string): ApiEndpoint[] => {
59
+ const endpointMap = new Map<string, ApiEndpoint>();
60
+
61
+ // Group endpoints by normalized path (without version)
62
+ const groupedEndpoints = new Map<string, ApiEndpoint[]>();
63
+
64
+ endpoints.forEach(endpoint => {
65
+ const normalizedPath = normalizeEndpointPath(endpoint.path);
66
+ if (!groupedEndpoints.has(normalizedPath)) {
67
+ groupedEndpoints.set(normalizedPath, []);
68
+ }
69
+ groupedEndpoints.get(normalizedPath)!.push(endpoint);
70
+ });
71
+
72
+ // For each group, pick the endpoint from preferred version
73
+ groupedEndpoints.forEach((endpointGroup, normalizedPath) => {
74
+ let selectedEndpoint: ApiEndpoint | null = null;
75
+
76
+ // Try to find endpoint in preferred version
77
+ const versionEndpoint = endpointGroup.find(ep => isEndpointInVersion(ep, preferredVersion));
78
+ if (versionEndpoint) {
79
+ selectedEndpoint = versionEndpoint;
80
+ } else if (endpointGroup.length > 0) {
81
+ // Fallback to first available endpoint
82
+ selectedEndpoint = endpointGroup[0] || null;
83
+ }
84
+
85
+ if (selectedEndpoint) {
86
+ endpointMap.set(normalizedPath, selectedEndpoint);
87
+ }
88
+ });
89
+
90
+ return Array.from(endpointMap.values());
91
+ };
92
+
93
+ /**
94
+ * Normalize endpoint path by removing version prefix
95
+ * /api/vehicles_api/v1/vehicles/ -> /api/vehicles_api/vehicles/
96
+ * /api/vehicles_api/vehicles/ -> /api/vehicles_api/vehicles/
97
+ */
98
+ export const normalizeEndpointPath = (path: string): string => {
99
+ // Remove version prefix like /v1/, /v2/, etc.
100
+ return path.replace(/\/v\d+\//, '/');
101
+ };
102
+
103
+ /**
104
+ * Convert endpoint path to specific version
105
+ */
106
+ export const convertEndpointToVersion = (endpoint: ApiEndpoint, targetVersion: string): ApiEndpoint => {
107
+ const currentVersion = detectEndpointVersion(endpoint.path);
108
+
109
+ // If already in target version, return as is
110
+ if (currentVersion === targetVersion) {
111
+ return endpoint;
112
+ }
113
+
114
+ let newPath = endpoint.path;
115
+
116
+ // Replace version: /api/vehicles_api/v1/vehicles/ -> /api/vehicles_api/v2/vehicles/
117
+ newPath = newPath.replace(/\/v\d+\//, `/${targetVersion}/`);
118
+
119
+ return {
120
+ ...endpoint,
121
+ path: newPath,
122
+ };
123
+ };
124
+
125
+ /**
126
+ * Get version info by ID
127
+ */
128
+ export const getVersionById = (versionId: string): ApiVersion | undefined => {
129
+ return API_VERSIONS.find(v => v.id === versionId);
130
+ };
131
+
132
+ /**
133
+ * Get default version
134
+ */
135
+ export const getDefaultVersion = (): ApiVersion => {
136
+ const defaultVersion = API_VERSIONS.find(v => v.isDefault);
137
+ if (defaultVersion) {
138
+ return defaultVersion;
139
+ }
140
+
141
+ // Fallback to first version if no default is set
142
+ if (API_VERSIONS.length > 0 && API_VERSIONS[0]) {
143
+ return API_VERSIONS[0];
144
+ }
145
+
146
+ // This should never happen, but TypeScript requires it
147
+ throw new Error('No API versions defined');
148
+ };
149
+
150
+ /**
151
+ * Get version statistics from endpoints
152
+ */
153
+ export const getVersionStats = (endpoints: ApiEndpoint[]): Record<string, number> => {
154
+ const stats: Record<string, number> = {};
155
+
156
+ API_VERSIONS.forEach(version => {
157
+ stats[version.id] = filterEndpointsByVersion(endpoints, version.id).length;
158
+ });
159
+
160
+ return stats;
161
+ };