@djangocfg/api 2.1.139 → 2.1.143

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 (55) hide show
  1. package/dist/auth-server.cjs +2 -199
  2. package/dist/auth-server.cjs.map +1 -1
  3. package/dist/auth-server.mjs +2 -199
  4. package/dist/auth-server.mjs.map +1 -1
  5. package/dist/auth.cjs +11 -836
  6. package/dist/auth.cjs.map +1 -1
  7. package/dist/auth.mjs +11 -836
  8. package/dist/auth.mjs.map +1 -1
  9. package/dist/clients.cjs +40 -1077
  10. package/dist/clients.cjs.map +1 -1
  11. package/dist/clients.d.cts +209 -754
  12. package/dist/clients.d.ts +209 -754
  13. package/dist/clients.mjs +40 -1077
  14. package/dist/clients.mjs.map +1 -1
  15. package/dist/index.cjs +0 -1198
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +229 -1211
  18. package/dist/index.d.ts +229 -1211
  19. package/dist/index.mjs +0 -1198
  20. package/dist/index.mjs.map +1 -1
  21. package/package.json +2 -2
  22. package/src/auth/hooks/useAuthForm.ts +1 -1
  23. package/src/auth/hooks/useAuthGuard.ts +1 -2
  24. package/src/auth/hooks/useAutoAuth.ts +1 -2
  25. package/src/auth/hooks/useDeleteAccount.ts +1 -1
  26. package/src/auth/hooks/useGithubAuth.ts +1 -2
  27. package/src/auth/hooks/useTokenRefresh.ts +1 -0
  28. package/src/auth/hooks/useTwoFactor.ts +1 -2
  29. package/src/clients.ts +2 -12
  30. package/src/index.ts +0 -6
  31. package/src/generated/cfg_webpush/CLAUDE.md +0 -63
  32. package/src/generated/cfg_webpush/_utils/fetchers/index.ts +0 -29
  33. package/src/generated/cfg_webpush/_utils/fetchers/webpush__web_push.ts +0 -211
  34. package/src/generated/cfg_webpush/_utils/hooks/index.ts +0 -29
  35. package/src/generated/cfg_webpush/_utils/hooks/webpush__web_push.ts +0 -79
  36. package/src/generated/cfg_webpush/_utils/schemas/SendPushRequestRequest.schema.ts +0 -22
  37. package/src/generated/cfg_webpush/_utils/schemas/SendPushResponse.schema.ts +0 -20
  38. package/src/generated/cfg_webpush/_utils/schemas/SubscribeRequestRequest.schema.ts +0 -20
  39. package/src/generated/cfg_webpush/_utils/schemas/SubscribeResponse.schema.ts +0 -21
  40. package/src/generated/cfg_webpush/_utils/schemas/VapidPublicKeyResponse.schema.ts +0 -19
  41. package/src/generated/cfg_webpush/_utils/schemas/index.ts +0 -24
  42. package/src/generated/cfg_webpush/api-instance.ts +0 -180
  43. package/src/generated/cfg_webpush/client.ts +0 -322
  44. package/src/generated/cfg_webpush/errors.ts +0 -117
  45. package/src/generated/cfg_webpush/http.ts +0 -110
  46. package/src/generated/cfg_webpush/index.ts +0 -275
  47. package/src/generated/cfg_webpush/logger.ts +0 -260
  48. package/src/generated/cfg_webpush/retry.ts +0 -176
  49. package/src/generated/cfg_webpush/schema.json +0 -302
  50. package/src/generated/cfg_webpush/storage.ts +0 -162
  51. package/src/generated/cfg_webpush/validation-events.ts +0 -134
  52. package/src/generated/cfg_webpush/webpush__web_push/client.ts +0 -45
  53. package/src/generated/cfg_webpush/webpush__web_push/index.ts +0 -3
  54. package/src/generated/cfg_webpush/webpush__web_push/models.ts +0 -65
  55. package/src/hooks/webpush.ts +0 -12
@@ -1,19 +0,0 @@
1
- /**
2
- * Zod schema for VapidPublicKeyResponse
3
- *
4
- * This schema provides runtime validation and type inference.
5
- * * Response serializer for VAPID public key endpoint.
6
- * */
7
- import { z } from 'zod'
8
-
9
- /**
10
- * Response serializer for VAPID public key endpoint.
11
- */
12
- export const VapidPublicKeyResponseSchema = z.object({
13
- publicKey: z.string(),
14
- })
15
-
16
- /**
17
- * Infer TypeScript type from Zod schema
18
- */
19
- export type VapidPublicKeyResponse = z.infer<typeof VapidPublicKeyResponseSchema>
@@ -1,24 +0,0 @@
1
- // Auto-generated by DjangoCFG - see CLAUDE.md
2
- /**
3
- * Zod Schemas - Runtime validation and type inference
4
- *
5
- * Auto-generated from OpenAPI specification.
6
- * Provides runtime validation for API requests and responses.
7
- *
8
- * Usage:
9
- * ```typescript
10
- * import { UserSchema } from './schemas'
11
- *
12
- * // Validate data
13
- * const user = UserSchema.parse(data)
14
- *
15
- * // Type inference
16
- * type User = z.infer<typeof UserSchema>
17
- * ```
18
- */
19
-
20
- export * from './SendPushRequestRequest.schema'
21
- export * from './SendPushResponse.schema'
22
- export * from './SubscribeRequestRequest.schema'
23
- export * from './SubscribeResponse.schema'
24
- export * from './VapidPublicKeyResponse.schema'
@@ -1,180 +0,0 @@
1
- // Auto-generated by DjangoCFG - see CLAUDE.md
2
- /**
3
- * Global API Instance - Singleton configuration with auto-configuration support
4
- *
5
- * This module provides a global API instance that auto-configures from
6
- * environment variables or can be configured manually.
7
- *
8
- * AUTO-CONFIGURATION (recommended):
9
- * Set one of these environment variables and the API will auto-configure:
10
- * - NEXT_PUBLIC_API_URL (Next.js)
11
- * - VITE_API_URL (Vite)
12
- * - REACT_APP_API_URL (Create React App)
13
- * - API_URL (generic)
14
- *
15
- * Then just use fetchers and hooks directly:
16
- * ```typescript
17
- * import { getUsers } from './_utils/fetchers'
18
- * const users = await getUsers({ page: 1 })
19
- * ```
20
- *
21
- * MANUAL CONFIGURATION:
22
- * ```typescript
23
- * import { configureAPI } from './api-instance'
24
- *
25
- * configureAPI({
26
- * baseUrl: 'https://api.example.com',
27
- * token: 'your-jwt-token'
28
- * })
29
- * ```
30
- *
31
- * For SSR or multiple instances:
32
- * ```typescript
33
- * import { API } from './index'
34
- * import { getUsers } from './_utils/fetchers'
35
- *
36
- * const api = new API('https://api.example.com')
37
- * const users = await getUsers({ page: 1 }, api)
38
- * ```
39
- */
40
-
41
- import { API, type APIOptions } from './index'
42
-
43
- let globalAPI: API | null = null
44
- let autoConfigAttempted = false
45
-
46
- /**
47
- * Auto-configure from environment variable if available (Next.js pattern)
48
- * This allows hooks and fetchers to work without explicit configureAPI() call
49
- *
50
- * Supported environment variables:
51
- * - NEXT_PUBLIC_API_URL (Next.js)
52
- * - VITE_API_URL (Vite)
53
- * - REACT_APP_API_URL (Create React App)
54
- * - API_URL (generic)
55
- */
56
- function tryAutoConfigureFromEnv(): void {
57
- // Only attempt once
58
- if (autoConfigAttempted) return
59
- autoConfigAttempted = true
60
-
61
- // Skip if already configured
62
- if (globalAPI) return
63
-
64
- // Skip if process is not available (pure browser without bundler)
65
- if (typeof process === 'undefined' || !process.env) return
66
-
67
- // Try different environment variable patterns
68
- const baseUrl =
69
- process.env.NEXT_PUBLIC_API_URL ||
70
- process.env.VITE_API_URL ||
71
- process.env.REACT_APP_API_URL ||
72
- process.env.API_URL
73
-
74
- if (baseUrl) {
75
- globalAPI = new API(baseUrl)
76
- }
77
- }
78
-
79
- /**
80
- * Get the global API instance
81
- * Auto-configures from environment variables on first call if not manually configured.
82
- * @throws Error if API is not configured and no env variable is set
83
- */
84
- export function getAPIInstance(): API {
85
- // Try auto-configuration on first access (lazy initialization)
86
- tryAutoConfigureFromEnv()
87
-
88
- if (!globalAPI) {
89
- throw new Error(
90
- 'API not configured. Call configureAPI() with your base URL before using fetchers or hooks.\n\n' +
91
- 'Example:\n' +
92
- ' import { configureAPI } from "./api-instance"\n' +
93
- ' configureAPI({ baseUrl: "https://api.example.com" })\n\n' +
94
- 'Or set environment variable: NEXT_PUBLIC_API_URL, VITE_API_URL, or REACT_APP_API_URL'
95
- )
96
- }
97
- return globalAPI
98
- }
99
-
100
- /**
101
- * Check if API is configured (or can be auto-configured)
102
- */
103
- export function isAPIConfigured(): boolean {
104
- tryAutoConfigureFromEnv()
105
- return globalAPI !== null
106
- }
107
-
108
- /**
109
- * Configure the global API instance
110
- *
111
- * @param baseUrl - Base URL for the API
112
- * @param options - Optional configuration (storage, retry, logger)
113
- *
114
- * @example
115
- * ```typescript
116
- * configureAPI({
117
- * baseUrl: 'https://api.example.com',
118
- * token: 'jwt-token',
119
- * options: {
120
- * retryConfig: { maxRetries: 3 },
121
- * loggerConfig: { enabled: true }
122
- * }
123
- * })
124
- * ```
125
- */
126
- export function configureAPI(config: {
127
- baseUrl: string
128
- token?: string
129
- refreshToken?: string
130
- options?: APIOptions
131
- }): API {
132
- globalAPI = new API(config.baseUrl, config.options)
133
-
134
- if (config.token) {
135
- globalAPI.setToken(config.token, config.refreshToken)
136
- }
137
-
138
- return globalAPI
139
- }
140
-
141
- /**
142
- * Reconfigure the global API instance with new settings
143
- * Useful for updating tokens or base URL
144
- */
145
- export function reconfigureAPI(updates: {
146
- baseUrl?: string
147
- token?: string
148
- refreshToken?: string
149
- }): API {
150
- const instance = getAPIInstance()
151
-
152
- if (updates.baseUrl) {
153
- instance.setBaseUrl(updates.baseUrl)
154
- }
155
-
156
- if (updates.token) {
157
- instance.setToken(updates.token, updates.refreshToken)
158
- }
159
-
160
- return instance
161
- }
162
-
163
- /**
164
- * Clear tokens from the global API instance
165
- */
166
- export function clearAPITokens(): void {
167
- const instance = getAPIInstance()
168
- instance.clearTokens()
169
- }
170
-
171
- /**
172
- * Reset the global API instance
173
- * Useful for testing or logout scenarios
174
- */
175
- export function resetAPI(): void {
176
- if (globalAPI) {
177
- globalAPI.clearTokens()
178
- }
179
- globalAPI = null
180
- }
@@ -1,322 +0,0 @@
1
- import { WebPush } from "./webpush__web_push";
2
- import { HttpClientAdapter, FetchAdapter } from "./http";
3
- import { APIError, NetworkError } from "./errors";
4
- import { APILogger, type LoggerConfig } from "./logger";
5
- import { withRetry, type RetryConfig } from "./retry";
6
-
7
-
8
- /**
9
- * Async API client for Django CFG API.
10
- *
11
- * Usage:
12
- * ```typescript
13
- * const client = new APIClient('https://api.example.com');
14
- * const users = await client.users.list();
15
- * const post = await client.posts.create(newPost);
16
- *
17
- * // Custom HTTP adapter (e.g., Axios)
18
- * const client = new APIClient('https://api.example.com', {
19
- * httpClient: new AxiosAdapter()
20
- * });
21
- * ```
22
- */
23
- export class APIClient {
24
- private baseUrl: string;
25
- private httpClient: HttpClientAdapter;
26
- private logger: APILogger | null = null;
27
- private retryConfig: RetryConfig | null = null;
28
- private tokenGetter: (() => string | null) | null = null;
29
-
30
- // Sub-clients
31
- public web_push: WebPush;
32
-
33
- constructor(
34
- baseUrl: string,
35
- options?: {
36
- httpClient?: HttpClientAdapter;
37
- loggerConfig?: Partial<LoggerConfig>;
38
- retryConfig?: RetryConfig;
39
- tokenGetter?: () => string | null;
40
- }
41
- ) {
42
- this.baseUrl = baseUrl.replace(/\/$/, '');
43
- this.httpClient = options?.httpClient || new FetchAdapter();
44
- this.tokenGetter = options?.tokenGetter || null;
45
-
46
- // Initialize logger if config provided
47
- if (options?.loggerConfig !== undefined) {
48
- this.logger = new APILogger(options.loggerConfig);
49
- }
50
-
51
- // Store retry configuration
52
- if (options?.retryConfig !== undefined) {
53
- this.retryConfig = options.retryConfig;
54
- }
55
-
56
- // Initialize sub-clients
57
- this.web_push = new WebPush(this);
58
- }
59
-
60
- /**
61
- * Get CSRF token from cookies (for SessionAuthentication).
62
- *
63
- * Returns null if cookie doesn't exist (JWT-only auth).
64
- */
65
- getCsrfToken(): string | null {
66
- const name = 'csrftoken';
67
- const value = `; ${document.cookie}`;
68
- const parts = value.split(`; ${name}=`);
69
- if (parts.length === 2) {
70
- return parts.pop()?.split(';').shift() || null;
71
- }
72
- return null;
73
- }
74
-
75
- /**
76
- * Get the base URL for building streaming/download URLs.
77
- */
78
- getBaseUrl(): string {
79
- return this.baseUrl;
80
- }
81
-
82
- /**
83
- * Get JWT token for URL authentication (used in streaming endpoints).
84
- * Returns null if no token getter is configured or no token is available.
85
- */
86
- getToken(): string | null {
87
- return this.tokenGetter ? this.tokenGetter() : null;
88
- }
89
-
90
- /**
91
- * Make HTTP request with Django CSRF and session handling.
92
- * Automatically retries on network errors and 5xx server errors.
93
- */
94
- async request<T>(
95
- method: string,
96
- path: string,
97
- options?: {
98
- params?: Record<string, any>;
99
- body?: any;
100
- formData?: FormData;
101
- binaryBody?: Blob | ArrayBuffer;
102
- headers?: Record<string, string>;
103
- }
104
- ): Promise<T> {
105
- // Wrap request in retry logic if configured
106
- if (this.retryConfig) {
107
- return withRetry(() => this._makeRequest<T>(method, path, options), {
108
- ...this.retryConfig,
109
- onFailedAttempt: (info) => {
110
- // Log retry attempts
111
- if (this.logger) {
112
- this.logger.warn(
113
- `Retry attempt ${info.attemptNumber}/${info.retriesLeft + info.attemptNumber} ` +
114
- `for ${method} ${path}: ${info.error.message}`
115
- );
116
- }
117
- // Call user's onFailedAttempt if provided
118
- this.retryConfig?.onFailedAttempt?.(info);
119
- },
120
- });
121
- }
122
-
123
- // No retry configured, make request directly
124
- return this._makeRequest<T>(method, path, options);
125
- }
126
-
127
- /**
128
- * Internal request method (without retry wrapper).
129
- * Used by request() method with optional retry logic.
130
- */
131
- private async _makeRequest<T>(
132
- method: string,
133
- path: string,
134
- options?: {
135
- params?: Record<string, any>;
136
- body?: any;
137
- formData?: FormData;
138
- binaryBody?: Blob | ArrayBuffer;
139
- headers?: Record<string, string>;
140
- }
141
- ): Promise<T> {
142
- // Build URL - handle both absolute and relative paths
143
- // When baseUrl is empty (static builds), path is used as-is (relative to current origin)
144
- const url = this.baseUrl ? `${this.baseUrl}${path}` : path;
145
- const startTime = Date.now();
146
-
147
- // Build headers - start with custom headers from options
148
- const headers: Record<string, string> = {
149
- ...(options?.headers || {})
150
- };
151
-
152
- // Don't set Content-Type for FormData/binaryBody (browser will set it with boundary)
153
- if (!options?.formData && !options?.binaryBody && !headers['Content-Type']) {
154
- headers['Content-Type'] = 'application/json';
155
- }
156
-
157
- // CSRF not needed - SessionAuthentication not enabled in DRF config
158
- // Your API uses JWT/Token authentication (no CSRF required)
159
-
160
- // Log request
161
- if (this.logger) {
162
- this.logger.logRequest({
163
- method,
164
- url: url,
165
- headers,
166
- body: options?.formData || options?.body,
167
- timestamp: startTime,
168
- });
169
- }
170
-
171
- try {
172
- // Make request via HTTP adapter
173
- const response = await this.httpClient.request<T>({
174
- method,
175
- url: url,
176
- headers,
177
- params: options?.params,
178
- body: options?.body,
179
- formData: options?.formData,
180
- binaryBody: options?.binaryBody,
181
- });
182
-
183
- const duration = Date.now() - startTime;
184
-
185
- // Check for HTTP errors
186
- if (response.status >= 400) {
187
- const error = new APIError(
188
- response.status,
189
- response.statusText,
190
- response.data,
191
- url
192
- );
193
-
194
- // Log error
195
- if (this.logger) {
196
- this.logger.logError(
197
- {
198
- method,
199
- url: url,
200
- headers,
201
- body: options?.formData || options?.body,
202
- timestamp: startTime,
203
- },
204
- {
205
- message: error.message,
206
- statusCode: response.status,
207
- duration,
208
- timestamp: Date.now(),
209
- }
210
- );
211
- }
212
-
213
- throw error;
214
- }
215
-
216
- // Log successful response
217
- if (this.logger) {
218
- this.logger.logResponse(
219
- {
220
- method,
221
- url: url,
222
- headers,
223
- body: options?.formData || options?.body,
224
- timestamp: startTime,
225
- },
226
- {
227
- status: response.status,
228
- statusText: response.statusText,
229
- data: response.data,
230
- duration,
231
- timestamp: Date.now(),
232
- }
233
- );
234
- }
235
-
236
- return response.data as T;
237
- } catch (error) {
238
- const duration = Date.now() - startTime;
239
-
240
- // Re-throw APIError as-is
241
- if (error instanceof APIError) {
242
- throw error;
243
- }
244
-
245
- // Detect CORS errors and dispatch event
246
- const isCORSError = error instanceof TypeError &&
247
- (error.message.toLowerCase().includes('cors') ||
248
- error.message.toLowerCase().includes('failed to fetch') ||
249
- error.message.toLowerCase().includes('network request failed'));
250
-
251
- // Log specific error type first
252
- if (this.logger) {
253
- if (isCORSError) {
254
- this.logger.error(`🚫 CORS Error: ${method} ${url}`);
255
- this.logger.error(` → ${error instanceof Error ? error.message : String(error)}`);
256
- this.logger.error(` → Configure security_domains parameter on the server`);
257
- } else {
258
- this.logger.error(`⚠️ Network Error: ${method} ${url}`);
259
- this.logger.error(` → ${error instanceof Error ? error.message : String(error)}`);
260
- }
261
- }
262
-
263
- // Dispatch browser events
264
- if (typeof window !== 'undefined') {
265
- try {
266
- if (isCORSError) {
267
- // Dispatch CORS-specific error event
268
- window.dispatchEvent(new CustomEvent('cors-error', {
269
- detail: {
270
- url: url,
271
- method: method,
272
- error: error instanceof Error ? error.message : String(error),
273
- timestamp: new Date(),
274
- },
275
- bubbles: true,
276
- cancelable: false,
277
- }));
278
- } else {
279
- // Dispatch generic network error event
280
- window.dispatchEvent(new CustomEvent('network-error', {
281
- detail: {
282
- url: url,
283
- method: method,
284
- error: error instanceof Error ? error.message : String(error),
285
- timestamp: new Date(),
286
- },
287
- bubbles: true,
288
- cancelable: false,
289
- }));
290
- }
291
- } catch (eventError) {
292
- // Silently fail - event dispatch should never crash the app
293
- }
294
- }
295
-
296
- // Wrap other errors as NetworkError
297
- const networkError = error instanceof Error
298
- ? new NetworkError(error.message, url, error)
299
- : new NetworkError('Unknown error', url);
300
-
301
- // Detailed logging via logger.logError
302
- if (this.logger) {
303
- this.logger.logError(
304
- {
305
- method,
306
- url: url,
307
- headers,
308
- body: options?.formData || options?.body,
309
- timestamp: startTime,
310
- },
311
- {
312
- message: networkError.message,
313
- duration,
314
- timestamp: Date.now(),
315
- }
316
- );
317
- }
318
-
319
- throw networkError;
320
- }
321
- }
322
- }
@@ -1,117 +0,0 @@
1
- // Auto-generated by DjangoCFG - see CLAUDE.md
2
- /**
3
- * API Error Classes
4
- *
5
- * Typed error classes with Django REST Framework support.
6
- */
7
-
8
- /**
9
- * HTTP API Error with DRF field-specific validation errors.
10
- *
11
- * Usage:
12
- * ```typescript
13
- * try {
14
- * await api.users.create(userData);
15
- * } catch (error) {
16
- * if (error instanceof APIError) {
17
- * if (error.isValidationError) {
18
- * console.log('Field errors:', error.fieldErrors);
19
- * // { "email": ["Email already exists"], "username": ["Required"] }
20
- * }
21
- * }
22
- * }
23
- * ```
24
- */
25
- export class APIError extends Error {
26
- constructor(
27
- public statusCode: number,
28
- public statusText: string,
29
- public response: any,
30
- public url: string,
31
- message?: string
32
- ) {
33
- super(message || `HTTP ${statusCode}: ${statusText}`);
34
- this.name = 'APIError';
35
- }
36
-
37
- /**
38
- * Get error details from response.
39
- * DRF typically returns: { "detail": "Error message" } or { "field": ["error1", "error2"] }
40
- */
41
- get details(): Record<string, any> | null {
42
- if (typeof this.response === 'object' && this.response !== null) {
43
- return this.response;
44
- }
45
- return null;
46
- }
47
-
48
- /**
49
- * Get field-specific validation errors from DRF.
50
- * Returns: { "field_name": ["error1", "error2"], ... }
51
- */
52
- get fieldErrors(): Record<string, string[]> | null {
53
- const details = this.details;
54
- if (!details) return null;
55
-
56
- // DRF typically returns: { "field": ["error1", "error2"] }
57
- const fieldErrors: Record<string, string[]> = {};
58
- for (const [key, value] of Object.entries(details)) {
59
- if (Array.isArray(value)) {
60
- fieldErrors[key] = value;
61
- }
62
- }
63
-
64
- return Object.keys(fieldErrors).length > 0 ? fieldErrors : null;
65
- }
66
-
67
- /**
68
- * Get single error message from DRF.
69
- * Checks for "detail", "message", or first field error.
70
- */
71
- get errorMessage(): string {
72
- const details = this.details;
73
- if (!details) return this.message;
74
-
75
- // Check for "detail" field (common in DRF)
76
- if (details.detail) {
77
- return Array.isArray(details.detail) ? details.detail.join(', ') : String(details.detail);
78
- }
79
-
80
- // Check for "message" field
81
- if (details.message) {
82
- return String(details.message);
83
- }
84
-
85
- // Return first field error
86
- const fieldErrors = this.fieldErrors;
87
- if (fieldErrors) {
88
- const firstField = Object.keys(fieldErrors)[0];
89
- if (firstField) {
90
- return `${firstField}: ${fieldErrors[firstField]?.join(', ')}`;
91
- }
92
- }
93
-
94
- return this.message;
95
- }
96
-
97
- // Helper methods for common HTTP status codes
98
- get isValidationError(): boolean { return this.statusCode === 400; }
99
- get isAuthError(): boolean { return this.statusCode === 401; }
100
- get isPermissionError(): boolean { return this.statusCode === 403; }
101
- get isNotFoundError(): boolean { return this.statusCode === 404; }
102
- get isServerError(): boolean { return this.statusCode >= 500 && this.statusCode < 600; }
103
- }
104
-
105
- /**
106
- * Network Error (connection failed, timeout, etc.)
107
- */
108
- export class NetworkError extends Error {
109
- constructor(
110
- message: string,
111
- public url: string,
112
- public originalError?: Error
113
- ) {
114
- super(message);
115
- this.name = 'NetworkError';
116
- }
117
- }