@pattern-stack/frontend-patterns 0.2.0-alpha.0 → 0.2.0-alpha.11

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 (160) hide show
  1. package/dist/atoms/components/core/Badge/Badge.d.ts +1 -1
  2. package/dist/atoms/components/data/DataTable/ColumnFilterDropdown.d.ts +32 -0
  3. package/dist/atoms/components/data/DataTable/ColumnFilterDropdown.d.ts.map +1 -0
  4. package/dist/atoms/components/data/DataTable/ColumnVisibilityToggle.d.ts +32 -0
  5. package/dist/atoms/components/data/DataTable/ColumnVisibilityToggle.d.ts.map +1 -0
  6. package/dist/atoms/components/data/DataTable/DataTable.d.ts +5 -2
  7. package/dist/atoms/components/data/DataTable/DataTable.d.ts.map +1 -1
  8. package/dist/atoms/components/data/DataTable/DataTable.expansion.d.ts +91 -0
  9. package/dist/atoms/components/data/DataTable/DataTable.expansion.d.ts.map +1 -0
  10. package/dist/atoms/components/data/DataTable/DataTable.filters.d.ts +271 -0
  11. package/dist/atoms/components/data/DataTable/DataTable.filters.d.ts.map +1 -0
  12. package/dist/atoms/components/data/DataTable/DataTable.types.d.ts +155 -5
  13. package/dist/atoms/components/data/DataTable/DataTable.types.d.ts.map +1 -1
  14. package/dist/atoms/components/data/DataTable/ExpandButton.d.ts +37 -0
  15. package/dist/atoms/components/data/DataTable/ExpandButton.d.ts.map +1 -0
  16. package/dist/atoms/components/data/DataTable/FilterPill.d.ts +25 -0
  17. package/dist/atoms/components/data/DataTable/FilterPill.d.ts.map +1 -0
  18. package/dist/atoms/components/data/DataTable/QuickFilterBar.d.ts +35 -0
  19. package/dist/atoms/components/data/DataTable/QuickFilterBar.d.ts.map +1 -0
  20. package/dist/atoms/components/data/DataTable/filters/BooleanFilterEditor.d.ts +10 -0
  21. package/dist/atoms/components/data/DataTable/filters/BooleanFilterEditor.d.ts.map +1 -0
  22. package/dist/atoms/components/data/DataTable/filters/DateFilterEditor.d.ts +11 -0
  23. package/dist/atoms/components/data/DataTable/filters/DateFilterEditor.d.ts.map +1 -0
  24. package/dist/atoms/components/data/DataTable/filters/MultiSelectFilterEditor.d.ts +10 -0
  25. package/dist/atoms/components/data/DataTable/filters/MultiSelectFilterEditor.d.ts.map +1 -0
  26. package/dist/atoms/components/data/DataTable/filters/NumberFilterEditor.d.ts +10 -0
  27. package/dist/atoms/components/data/DataTable/filters/NumberFilterEditor.d.ts.map +1 -0
  28. package/dist/atoms/components/data/DataTable/filters/SelectFilterEditor.d.ts +10 -0
  29. package/dist/atoms/components/data/DataTable/filters/SelectFilterEditor.d.ts.map +1 -0
  30. package/dist/atoms/components/data/DataTable/filters/TextFilterEditor.d.ts +10 -0
  31. package/dist/atoms/components/data/DataTable/filters/TextFilterEditor.d.ts.map +1 -0
  32. package/dist/atoms/components/data/DataTable/filters/index.d.ts +14 -0
  33. package/dist/atoms/components/data/DataTable/filters/index.d.ts.map +1 -0
  34. package/dist/atoms/components/data/DataTable/index.d.ts +9 -0
  35. package/dist/atoms/components/data/DataTable/index.d.ts.map +1 -1
  36. package/dist/atoms/components/data/ProgressBar/ProgressBar.d.ts +1 -1
  37. package/dist/atoms/components/data/ProgressBar/ProgressBar.d.ts.map +1 -1
  38. package/dist/atoms/components/data/index.d.ts +3 -2
  39. package/dist/atoms/components/data/index.d.ts.map +1 -1
  40. package/dist/atoms/composed/ConnectionStatus/ConnectionStatus.d.ts +16 -0
  41. package/dist/atoms/composed/ConnectionStatus/ConnectionStatus.d.ts.map +1 -0
  42. package/dist/atoms/composed/ConnectionStatus/index.d.ts +3 -0
  43. package/dist/atoms/composed/ConnectionStatus/index.d.ts.map +1 -0
  44. package/dist/atoms/hooks/index.d.ts +9 -0
  45. package/dist/atoms/hooks/index.d.ts.map +1 -1
  46. package/dist/atoms/hooks/useAdaptiveTable.d.ts +49 -0
  47. package/dist/atoms/hooks/useAdaptiveTable.d.ts.map +1 -0
  48. package/dist/atoms/hooks/useApi.d.ts +1 -1
  49. package/dist/atoms/hooks/useApi.d.ts.map +1 -1
  50. package/dist/atoms/hooks/useColumnVisibility.d.ts +75 -0
  51. package/dist/atoms/hooks/useColumnVisibility.d.ts.map +1 -0
  52. package/dist/atoms/hooks/useEntityData.d.ts +36 -0
  53. package/dist/atoms/hooks/useEntityData.d.ts.map +1 -0
  54. package/dist/atoms/hooks/useEntityDetail.d.ts +43 -0
  55. package/dist/atoms/hooks/useEntityDetail.d.ts.map +1 -0
  56. package/dist/atoms/hooks/useExpandedRows.d.ts +66 -0
  57. package/dist/atoms/hooks/useExpandedRows.d.ts.map +1 -0
  58. package/dist/atoms/hooks/useFieldMetadata.d.ts +18 -0
  59. package/dist/atoms/hooks/useFieldMetadata.d.ts.map +1 -0
  60. package/dist/atoms/hooks/useOnlineStatus.d.ts +16 -0
  61. package/dist/atoms/hooks/useOnlineStatus.d.ts.map +1 -0
  62. package/dist/atoms/hooks/useResponsiveTable.d.ts +123 -0
  63. package/dist/atoms/hooks/useResponsiveTable.d.ts.map +1 -0
  64. package/dist/atoms/hooks/useTableFilters.d.ts +92 -0
  65. package/dist/atoms/hooks/useTableFilters.d.ts.map +1 -0
  66. package/dist/atoms/index.d.ts +1 -0
  67. package/dist/atoms/index.d.ts.map +1 -1
  68. package/dist/atoms/primitives/sheet.d.ts +23 -0
  69. package/dist/atoms/primitives/sheet.d.ts.map +1 -0
  70. package/dist/atoms/primitives/table.d.ts.map +1 -1
  71. package/dist/atoms/services/api/client.d.ts +12 -2
  72. package/dist/atoms/services/api/client.d.ts.map +1 -1
  73. package/dist/atoms/services/auth-service.d.ts +15 -0
  74. package/dist/atoms/services/auth-service.d.ts.map +1 -1
  75. package/dist/atoms/services/index.d.ts +2 -2
  76. package/dist/atoms/services/index.d.ts.map +1 -1
  77. package/dist/atoms/shared/config/table-config.d.ts +79 -0
  78. package/dist/atoms/shared/config/table-config.d.ts.map +1 -0
  79. package/dist/atoms/shared/index.d.ts +1 -0
  80. package/dist/atoms/shared/index.d.ts.map +1 -1
  81. package/dist/atoms/types/auth.d.ts +95 -2
  82. package/dist/atoms/types/auth.d.ts.map +1 -1
  83. package/dist/atoms/types/index.d.ts +1 -0
  84. package/dist/atoms/types/index.d.ts.map +1 -1
  85. package/dist/atoms/types/navigation.d.ts +1 -1
  86. package/dist/atoms/types/navigation.d.ts.map +1 -1
  87. package/dist/atoms/types/ui-config.d.ts +46 -11
  88. package/dist/atoms/types/ui-config.d.ts.map +1 -1
  89. package/dist/atoms/types/ui-metadata.d.ts +103 -0
  90. package/dist/atoms/types/ui-metadata.d.ts.map +1 -0
  91. package/dist/atoms/utils/entity-card-mapping.d.ts +105 -0
  92. package/dist/atoms/utils/entity-card-mapping.d.ts.map +1 -0
  93. package/dist/atoms/utils/field-detection.d.ts +2 -2
  94. package/dist/atoms/utils/field-detection.d.ts.map +1 -1
  95. package/dist/atoms/utils/icon-map.d.ts +48 -0
  96. package/dist/atoms/utils/icon-map.d.ts.map +1 -1
  97. package/dist/atoms/utils/index.d.ts +2 -0
  98. package/dist/atoms/utils/index.d.ts.map +1 -1
  99. package/dist/atoms/utils/ui-mapping.d.ts +9 -3
  100. package/dist/atoms/utils/ui-mapping.d.ts.map +1 -1
  101. package/dist/features/auth/components/ProtectedRoute.d.ts +3 -1
  102. package/dist/features/auth/components/ProtectedRoute.d.ts.map +1 -1
  103. package/dist/features/auth/hooks/useAuth.d.ts.map +1 -1
  104. package/dist/features/auth/providers/NoAuthProvider.d.ts +17 -0
  105. package/dist/features/auth/providers/NoAuthProvider.d.ts.map +1 -0
  106. package/dist/features/auth/providers/index.d.ts +1 -0
  107. package/dist/features/auth/providers/index.d.ts.map +1 -1
  108. package/dist/frontend-patterns.css +1 -4554
  109. package/dist/index.d.ts +12 -4
  110. package/dist/index.d.ts.map +1 -1
  111. package/dist/index.es.js +8793 -18275
  112. package/dist/index.es.js.map +1 -1
  113. package/dist/index.js +8790 -18271
  114. package/dist/index.js.map +1 -1
  115. package/dist/molecules/layout/AppHeader/AppHeader.d.ts.map +1 -1
  116. package/dist/molecules/layout/BulkSelectionBar.d.ts +14 -2
  117. package/dist/molecules/layout/BulkSelectionBar.d.ts.map +1 -1
  118. package/dist/molecules/layout/FieldGrid/FieldGrid.d.ts +61 -0
  119. package/dist/molecules/layout/FieldGrid/FieldGrid.d.ts.map +1 -0
  120. package/dist/molecules/layout/FieldGrid/index.d.ts +2 -0
  121. package/dist/molecules/layout/FieldGrid/index.d.ts.map +1 -0
  122. package/dist/molecules/layout/ListToolbar/ListToolbar.d.ts +37 -0
  123. package/dist/molecules/layout/ListToolbar/ListToolbar.d.ts.map +1 -0
  124. package/dist/molecules/layout/ListToolbar/index.d.ts +2 -0
  125. package/dist/molecules/layout/ListToolbar/index.d.ts.map +1 -0
  126. package/dist/molecules/layout/PageTitle/PageTitle.d.ts +17 -0
  127. package/dist/molecules/layout/PageTitle/PageTitle.d.ts.map +1 -0
  128. package/dist/molecules/layout/PageTitle/index.d.ts +2 -0
  129. package/dist/molecules/layout/PageTitle/index.d.ts.map +1 -0
  130. package/dist/molecules/layout/index.d.ts +3 -0
  131. package/dist/molecules/layout/index.d.ts.map +1 -1
  132. package/dist/molecules/layout/navigation-context.d.ts.map +1 -1
  133. package/dist/sync/EntityStoreProvider.d.ts +35 -0
  134. package/dist/sync/EntityStoreProvider.d.ts.map +1 -0
  135. package/dist/sync/createEntityHooks.d.ts +29 -0
  136. package/dist/sync/createEntityHooks.d.ts.map +1 -0
  137. package/dist/sync/createStore.d.ts +65 -0
  138. package/dist/sync/createStore.d.ts.map +1 -0
  139. package/dist/sync/index.d.ts +6 -0
  140. package/dist/sync/index.d.ts.map +1 -0
  141. package/dist/sync/types.d.ts +383 -0
  142. package/dist/sync/types.d.ts.map +1 -0
  143. package/dist/templates/ListPageTemplate.d.ts +21 -0
  144. package/dist/templates/ListPageTemplate.d.ts.map +1 -0
  145. package/dist/templates/admin/AdminCRUDTemplate.d.ts.map +1 -1
  146. package/dist/templates/factory.d.ts +11 -0
  147. package/dist/templates/factory.d.ts.map +1 -1
  148. package/dist/templates/index.d.ts +1 -0
  149. package/dist/templates/index.d.ts.map +1 -1
  150. package/package.json +11 -7
  151. package/cli/commands/generate-hooks.ts +0 -316
  152. package/cli/commands/init.ts +0 -33
  153. package/cli/commands/scaffold.ts +0 -224
  154. package/cli/index.ts +0 -122
  155. package/cli/src/codegen/openapi/client-generator.js +0 -659
  156. package/cli/src/codegen/openapi/hook-generator.js +0 -725
  157. package/cli/src/codegen/openapi/parser.js +0 -274
  158. package/cli/src/codegen/openapi/type-generator.js +0 -329
  159. package/dist/codegen/openapi/bulk-types.d.ts +0 -142
  160. package/dist/codegen/openapi/bulk-types.d.ts.map +0 -1
@@ -1,659 +0,0 @@
1
- /**
2
- * API Client Generator
3
- *
4
- * Creates a type-safe API client with interceptors and error handling
5
- * based on parsed OpenAPI specifications.
6
- *
7
- * Part of FRO-12: API Client Generator
8
- */
9
- export class APIClientGenerator {
10
- options;
11
- constructor(options = {}) {
12
- this.options = {
13
- clientType: options.clientType || 'axios',
14
- baseUrl: options.baseUrl || '',
15
- includeAuth: options.includeAuth !== false,
16
- authType: options.authType || 'bearer',
17
- timeout: options.timeout || 10000,
18
- retries: options.retries || 3,
19
- includeInterceptors: options.includeInterceptors !== false
20
- };
21
- }
22
- generate(parsedAPI) {
23
- return {
24
- client: this.generateClientSetup(),
25
- methods: this.generateApiMethods(parsedAPI.endpoints),
26
- types: this.generateClientTypes(),
27
- config: this.generateConfiguration(),
28
- index: this.generateIndexFile()
29
- };
30
- }
31
- generateClientSetup() {
32
- if (this.options.clientType === 'axios') {
33
- return this.generateAxiosClient();
34
- }
35
- else {
36
- return this.generateFetchClient();
37
- }
38
- }
39
- generateAxiosClient() {
40
- return `/**
41
- * Axios API Client
42
- *
43
- * Auto-generated API client with interceptors and error handling
44
- */
45
-
46
- import axios, { AxiosInstance, AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios'
47
- import { APIClientConfig, RequestOptions, APIError } from './types'
48
-
49
- export class APIClient {
50
- private client: AxiosInstance
51
- private config: APIClientConfig
52
-
53
- constructor(config: APIClientConfig) {
54
- this.config = config
55
- this.client = this.createAxiosInstance()
56
- this.setupInterceptors()
57
- }
58
-
59
- private createAxiosInstance(): AxiosInstance {
60
- return axios.create({
61
- baseURL: this.config.baseUrl,
62
- timeout: this.config.timeout || ${this.options.timeout},
63
- headers: {
64
- 'Content-Type': 'application/json',
65
- ...this.config.defaultHeaders
66
- }
67
- })
68
- }
69
-
70
- ${this.options.includeInterceptors ? this.generateAxiosInterceptors() : ''}
71
-
72
- private async makeRequest<T>(
73
- method: string,
74
- url: string,
75
- options: RequestOptions = {}
76
- ): Promise<T> {
77
- try {
78
- const config: AxiosRequestConfig = {
79
- method: method as any,
80
- url,
81
- ...options.config
82
- }
83
-
84
- if (options.params) {
85
- config.params = options.params
86
- }
87
-
88
- if (options.data) {
89
- config.data = options.data
90
- }
91
-
92
- if (options.headers) {
93
- config.headers = { ...config.headers, ...options.headers }
94
- }
95
-
96
- ${this.options.includeAuth ? this.generateAuthInjection() : ''}
97
-
98
- const response: AxiosResponse<T> = await this.client.request(config)
99
- return response.data
100
- } catch (error) {
101
- throw this.handleError(error as AxiosError)
102
- }
103
- }
104
-
105
- private handleError(error: AxiosError): APIError {
106
- const apiError: APIError = {
107
- message: error.message,
108
- status: error.response?.status,
109
- statusText: error.response?.statusText,
110
- data: error.response?.data,
111
- config: {
112
- url: error.config?.url,
113
- method: error.config?.method?.toUpperCase(),
114
- headers: error.config?.headers
115
- }
116
- }
117
-
118
- if (this.config.onError) {
119
- this.config.onError(apiError)
120
- }
121
-
122
- return apiError
123
- }
124
-
125
- public get<T>(url: string, options?: RequestOptions): Promise<T> {
126
- return this.makeRequest<T>('GET', url, options)
127
- }
128
-
129
- public post<T>(url: string, data?: any, options?: RequestOptions): Promise<T> {
130
- return this.makeRequest<T>('POST', url, { ...options, data })
131
- }
132
-
133
- public put<T>(url: string, data?: any, options?: RequestOptions): Promise<T> {
134
- return this.makeRequest<T>('PUT', url, { ...options, data })
135
- }
136
-
137
- public patch<T>(url: string, data?: any, options?: RequestOptions): Promise<T> {
138
- return this.makeRequest<T>('PATCH', url, { ...options, data })
139
- }
140
-
141
- public delete<T>(url: string, options?: RequestOptions): Promise<T> {
142
- return this.makeRequest<T>('DELETE', url, options)
143
- }
144
- }`;
145
- }
146
- generateFetchClient() {
147
- return `/**
148
- * Fetch API Client
149
- *
150
- * Auto-generated API client using native fetch with error handling
151
- */
152
-
153
- import { APIClientConfig, RequestOptions, APIError } from './types'
154
-
155
- export class APIClient {
156
- private config: APIClientConfig
157
-
158
- constructor(config: APIClientConfig) {
159
- this.config = config
160
- }
161
-
162
- private async makeRequest<T>(
163
- method: string,
164
- url: string,
165
- options: RequestOptions = {}
166
- ): Promise<T> {
167
- try {
168
- const fullUrl = this.buildUrl(url, options.params)
169
-
170
- const fetchOptions: RequestInit = {
171
- method,
172
- headers: {
173
- 'Content-Type': 'application/json',
174
- ...this.config.defaultHeaders,
175
- ...options.headers
176
- },
177
- signal: options.signal
178
- }
179
-
180
- if (options.data && method !== 'GET') {
181
- fetchOptions.body = JSON.stringify(options.data)
182
- }
183
-
184
- ${this.options.includeAuth ? this.generateFetchAuthInjection() : ''}
185
-
186
- const response = await fetch(fullUrl, fetchOptions)
187
-
188
- if (!response.ok) {
189
- throw await this.createErrorFromResponse(response)
190
- }
191
-
192
- const contentType = response.headers.get('content-type')
193
- if (contentType && contentType.includes('application/json')) {
194
- return await response.json()
195
- }
196
-
197
- return await response.text() as any
198
- } catch (error) {
199
- if (error instanceof APIError) {
200
- throw error
201
- }
202
- throw this.createGenericError(error as Error)
203
- }
204
- }
205
-
206
- private buildUrl(path: string, params?: Record<string, any>): string {
207
- const url = new URL(path, this.config.baseUrl)
208
-
209
- if (params) {
210
- Object.entries(params).forEach(([key, value]) => {
211
- if (value !== undefined && value !== null) {
212
- url.searchParams.append(key, String(value))
213
- }
214
- })
215
- }
216
-
217
- return url.toString()
218
- }
219
-
220
- private async createErrorFromResponse(response: Response): Promise<APIError> {
221
- let data: any
222
- try {
223
- data = await response.json()
224
- } catch {
225
- data = await response.text()
226
- }
227
-
228
- const error: APIError = {
229
- message: data?.message || response.statusText || 'Request failed',
230
- status: response.status,
231
- statusText: response.statusText,
232
- data,
233
- config: {
234
- url: response.url,
235
- method: 'Unknown'
236
- }
237
- }
238
-
239
- if (this.config.onError) {
240
- this.config.onError(error)
241
- }
242
-
243
- return error
244
- }
245
-
246
- private createGenericError(error: Error): APIError {
247
- const apiError: APIError = {
248
- message: error.message,
249
- config: {}
250
- }
251
-
252
- if (this.config.onError) {
253
- this.config.onError(apiError)
254
- }
255
-
256
- return apiError
257
- }
258
-
259
- public get<T>(url: string, options?: RequestOptions): Promise<T> {
260
- return this.makeRequest<T>('GET', url, options)
261
- }
262
-
263
- public post<T>(url: string, data?: any, options?: RequestOptions): Promise<T> {
264
- return this.makeRequest<T>('POST', url, { ...options, data })
265
- }
266
-
267
- public put<T>(url: string, data?: any, options?: RequestOptions): Promise<T> {
268
- return this.makeRequest<T>('PUT', url, { ...options, data })
269
- }
270
-
271
- public patch<T>(url: string, data?: any, options?: RequestOptions): Promise<T> {
272
- return this.makeRequest<T>('PATCH', url, { ...options, data })
273
- }
274
-
275
- public delete<T>(url: string, options?: RequestOptions): Promise<T> {
276
- return this.makeRequest<T>('DELETE', url, options)
277
- }
278
- }`;
279
- }
280
- generateAxiosInterceptors() {
281
- return `
282
- private setupInterceptors(): void {
283
- // Request interceptor
284
- this.client.interceptors.request.use(
285
- (config) => {
286
- if (this.config.onRequest) {
287
- return this.config.onRequest(config) || config
288
- }
289
- return config
290
- },
291
- (error) => {
292
- if (this.config.onRequestError) {
293
- this.config.onRequestError(error)
294
- }
295
- return Promise.reject(error)
296
- }
297
- )
298
-
299
- // Response interceptor
300
- this.client.interceptors.response.use(
301
- (response) => {
302
- if (this.config.onResponse) {
303
- return this.config.onResponse(response) || response
304
- }
305
- return response
306
- },
307
- async (error) => {
308
- if (this.config.onResponseError) {
309
- const result = await this.config.onResponseError(error)
310
- if (result) {
311
- return result
312
- }
313
- }
314
-
315
- // Auto-retry logic
316
- if (this.shouldRetry(error)) {
317
- return this.retryRequest(error.config)
318
- }
319
-
320
- return Promise.reject(error)
321
- }
322
- )
323
- }
324
-
325
- private shouldRetry(error: AxiosError): boolean {
326
- const retryCount = (error.config as any)?._retryCount || 0
327
- const maxRetries = this.config.retries || ${this.options.retries}
328
-
329
- return (
330
- retryCount < maxRetries &&
331
- (!error.response || error.response.status >= 500) &&
332
- error.code !== 'ECONNABORTED'
333
- )
334
- }
335
-
336
- private async retryRequest(config: any): Promise<any> {
337
- config._retryCount = (config._retryCount || 0) + 1
338
-
339
- // Exponential backoff
340
- const delay = Math.pow(2, config._retryCount) * 1000
341
- await new Promise(resolve => setTimeout(resolve, delay))
342
-
343
- return this.client.request(config)
344
- }`;
345
- }
346
- generateAuthInjection() {
347
- switch (this.options.authType) {
348
- case 'bearer':
349
- return `
350
- // Inject bearer token if available
351
- if (this.config.getAuthToken) {
352
- const token = await this.config.getAuthToken()
353
- if (token) {
354
- config.headers = {
355
- ...config.headers,
356
- Authorization: \`Bearer \${token}\`
357
- }
358
- }
359
- }`;
360
- case 'apiKey':
361
- return `
362
- // Inject API key if available
363
- if (this.config.getApiKey) {
364
- const apiKey = await this.config.getApiKey()
365
- if (apiKey) {
366
- if (this.config.apiKeyHeader) {
367
- config.headers = {
368
- ...config.headers,
369
- [this.config.apiKeyHeader]: apiKey
370
- }
371
- } else {
372
- config.params = { ...config.params, api_key: apiKey }
373
- }
374
- }
375
- }`;
376
- case 'basic':
377
- return `
378
- // Inject basic auth if available
379
- if (this.config.getBasicAuth) {
380
- const auth = await this.config.getBasicAuth()
381
- if (auth) {
382
- const encoded = btoa(\`\${auth.username}:\${auth.password}\`)
383
- config.headers = {
384
- ...config.headers,
385
- Authorization: \`Basic \${encoded}\`
386
- }
387
- }
388
- }`;
389
- default:
390
- return '';
391
- }
392
- }
393
- generateFetchAuthInjection() {
394
- switch (this.options.authType) {
395
- case 'bearer':
396
- return `
397
- // Inject bearer token if available
398
- if (this.config.getAuthToken) {
399
- const token = await this.config.getAuthToken()
400
- if (token) {
401
- fetchOptions.headers = {
402
- ...fetchOptions.headers,
403
- Authorization: \`Bearer \${token}\`
404
- }
405
- }
406
- }`;
407
- case 'apiKey':
408
- return `
409
- // Inject API key if available
410
- if (this.config.getApiKey) {
411
- const apiKey = await this.config.getApiKey()
412
- if (apiKey && this.config.apiKeyHeader) {
413
- fetchOptions.headers = {
414
- ...fetchOptions.headers,
415
- [this.config.apiKeyHeader]: apiKey
416
- }
417
- }
418
- }`;
419
- default:
420
- return '';
421
- }
422
- }
423
- generateApiMethods(endpoints) {
424
- const methods = [];
425
- methods.push(this.generateFileHeader('API Methods'));
426
- methods.push('');
427
- methods.push('import { APIClient } from \'./client\'');
428
- methods.push('import * as Types from \'./types\'');
429
- methods.push('');
430
- methods.push('export class APIService {');
431
- methods.push(' constructor(private client: APIClient) {}');
432
- methods.push('');
433
- // Group endpoints by tags or path prefix
434
- const groupedEndpoints = this.groupEndpoints(endpoints);
435
- for (const [group, groupEndpoints] of Object.entries(groupedEndpoints)) {
436
- methods.push(` // ${group} methods`);
437
- for (const endpoint of groupEndpoints) {
438
- const method = this.generateEndpointMethod(endpoint);
439
- methods.push(method);
440
- methods.push('');
441
- }
442
- }
443
- methods.push('}');
444
- return methods.join('\n');
445
- }
446
- generateEndpointMethod(endpoint) {
447
- const methodName = this.getMethodName(endpoint);
448
- const pathWithParams = this.generatePathWithParams(endpoint);
449
- const httpMethod = endpoint.method.toLowerCase();
450
- const lines = [];
451
- // Generate JSDoc
452
- lines.push(' /**');
453
- if (endpoint.summary) {
454
- lines.push(` * ${endpoint.summary}`);
455
- }
456
- if (endpoint.description) {
457
- lines.push(` * ${endpoint.description}`);
458
- }
459
- lines.push(` * @param options Request options`);
460
- lines.push(' */');
461
- // Generate method signature
462
- const hasPathParams = endpoint.parameters.some(p => p.in === 'path');
463
- const hasQueryParams = endpoint.parameters.some(p => p.in === 'query');
464
- const hasBody = endpoint.requestBody !== undefined;
465
- let params = 'options: RequestOptions = {}';
466
- if (hasPathParams) {
467
- const pathParams = endpoint.parameters
468
- .filter(p => p.in === 'path')
469
- .map(p => `${p.name}: ${this.getParameterType(p)}`)
470
- .join(', ');
471
- params = `${pathParams}, ${params}`;
472
- }
473
- const responseType = this.getResponseType(endpoint);
474
- lines.push(` async ${methodName}(${params}): Promise<${responseType}> {`);
475
- // Generate method body
476
- const urlConstruction = hasPathParams ?
477
- `const url = \`${pathWithParams}\`` :
478
- `const url = '${endpoint.path}'`;
479
- lines.push(` ${urlConstruction}`);
480
- if (hasQueryParams) {
481
- lines.push(' const queryParams = {');
482
- endpoint.parameters
483
- .filter(p => p.in === 'query')
484
- .forEach(p => {
485
- lines.push(` ${p.name}: options.${p.name},`);
486
- });
487
- lines.push(' }');
488
- lines.push(' options.params = { ...queryParams, ...options.params }');
489
- }
490
- if (hasBody) {
491
- lines.push(` return this.client.${httpMethod}<${responseType}>(url, options.data, options)`);
492
- }
493
- else {
494
- lines.push(` return this.client.${httpMethod}<${responseType}>(url, options)`);
495
- }
496
- lines.push(' }');
497
- return lines.join('\n');
498
- }
499
- generateClientTypes() {
500
- const authTypes = this.generateAuthTypes();
501
- return `/**
502
- * API Client Types
503
- *
504
- * Configuration and utility types for the generated API client
505
- */
506
-
507
- ${this.options.clientType === 'axios' ? "import { AxiosRequestConfig, AxiosResponse } from 'axios'" : ''}
508
-
509
- export interface APIClientConfig {
510
- baseUrl: string
511
- timeout?: number
512
- defaultHeaders?: Record<string, string>
513
- retries?: number
514
- ${this.options.includeAuth ? authTypes : ''}
515
- onRequest?: (config: any) => any
516
- onRequestError?: (error: any) => void
517
- onResponse?: (response: any) => any
518
- onResponseError?: (error: any) => Promise<any> | any
519
- onError?: (error: APIError) => void
520
- }
521
-
522
- export interface RequestOptions {
523
- params?: Record<string, any>
524
- data?: any
525
- headers?: Record<string, string>
526
- config?: any
527
- signal?: AbortSignal
528
- [key: string]: any
529
- }
530
-
531
- export interface APIError {
532
- message: string
533
- status?: number
534
- statusText?: string
535
- data?: any
536
- config: {
537
- url?: string
538
- method?: string
539
- headers?: any
540
- }
541
- }`;
542
- }
543
- generateAuthTypes() {
544
- switch (this.options.authType) {
545
- case 'bearer':
546
- return 'getAuthToken?: () => Promise<string | null> | string | null';
547
- case 'apiKey':
548
- return `getApiKey?: () => Promise<string | null> | string | null
549
- apiKeyHeader?: string`;
550
- case 'basic':
551
- return 'getBasicAuth?: () => Promise<{ username: string; password: string } | null> | { username: string; password: string } | null';
552
- default:
553
- return '';
554
- }
555
- }
556
- generateConfiguration() {
557
- return `/**
558
- * API Client Configuration
559
- *
560
- * Default configuration and factory functions
561
- */
562
-
563
- import { APIClient } from './client'
564
- import { APIService } from './methods'
565
- import { APIClientConfig } from './types'
566
-
567
- export function createAPIClient(config: APIClientConfig): APIService {
568
- const client = new APIClient(config)
569
- return new APIService(client)
570
- }
571
-
572
- export const defaultConfig: Partial<APIClientConfig> = {
573
- timeout: ${this.options.timeout},
574
- retries: ${this.options.retries},
575
- defaultHeaders: {
576
- 'Content-Type': 'application/json'
577
- }
578
- }`;
579
- }
580
- generateIndexFile() {
581
- return `/**
582
- * Generated API Client
583
- *
584
- * Auto-generated from OpenAPI specification
585
- */
586
-
587
- export * from './client'
588
- export * from './methods'
589
- export * from './types'
590
- export * from './config'
591
- export { createAPIClient, defaultConfig } from './config'`;
592
- }
593
- generateFileHeader(title) {
594
- return `/**
595
- * ${title}
596
- *
597
- * Auto-generated from OpenAPI specification
598
- * Do not edit manually
599
- */`;
600
- }
601
- getMethodName(endpoint) {
602
- if (endpoint.operationId) {
603
- return this.camelCase(endpoint.operationId);
604
- }
605
- const pathParts = endpoint.path
606
- .split('/')
607
- .filter(part => part && !part.startsWith('{'))
608
- .map(part => this.capitalize(part));
609
- return this.camelCase(`${endpoint.method}_${pathParts.join('_')}`);
610
- }
611
- generatePathWithParams(endpoint) {
612
- return endpoint.path.replace(/{([^}]+)}/g, '${$1}');
613
- }
614
- getParameterType(param) {
615
- switch (param.schema.type) {
616
- case 'string': return 'string';
617
- case 'number':
618
- case 'integer': return 'number';
619
- case 'boolean': return 'boolean';
620
- default: return 'any';
621
- }
622
- }
623
- getResponseType(endpoint) {
624
- // Find success response (2xx)
625
- const successResponse = endpoint.responses.find(r => r.statusCode.startsWith('2') || r.statusCode === 'default');
626
- if (!successResponse?.content) {
627
- return 'void';
628
- }
629
- const jsonContent = successResponse.content['application/json'];
630
- if (!jsonContent) {
631
- return 'any';
632
- }
633
- // This would need to be enhanced to generate proper type references
634
- return 'any';
635
- }
636
- groupEndpoints(endpoints) {
637
- const groups = {};
638
- for (const endpoint of endpoints) {
639
- const group = endpoint.tags?.[0] || 'default';
640
- if (!groups[group]) {
641
- groups[group] = [];
642
- }
643
- groups[group].push(endpoint);
644
- }
645
- return groups;
646
- }
647
- camelCase(str) {
648
- return str.replace(/[-_](.)/g, (_, char) => char.toUpperCase())
649
- .replace(/^./, char => char.toLowerCase());
650
- }
651
- capitalize(str) {
652
- return str.charAt(0).toUpperCase() + str.slice(1);
653
- }
654
- }
655
- // Factory function
656
- export function generateAPIClient(parsedAPI, options) {
657
- const generator = new APIClientGenerator(options);
658
- return generator.generate(parsedAPI);
659
- }