@djangocfg/api 2.1.88 → 2.1.90

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 (53) hide show
  1. package/dist/auth-server.cjs +335 -320
  2. package/dist/auth-server.cjs.map +1 -1
  3. package/dist/auth-server.mjs +335 -320
  4. package/dist/auth-server.mjs.map +1 -1
  5. package/dist/auth.cjs +464 -394
  6. package/dist/auth.cjs.map +1 -1
  7. package/dist/auth.d.cts +1 -1
  8. package/dist/auth.d.ts +1 -1
  9. package/dist/auth.mjs +464 -394
  10. package/dist/auth.mjs.map +1 -1
  11. package/dist/clients.cjs +460 -383
  12. package/dist/clients.cjs.map +1 -1
  13. package/dist/clients.d.cts +26 -6
  14. package/dist/clients.d.ts +26 -6
  15. package/dist/clients.mjs +460 -383
  16. package/dist/clients.mjs.map +1 -1
  17. package/dist/hooks.cjs +130 -105
  18. package/dist/hooks.cjs.map +1 -1
  19. package/dist/hooks.d.cts +24 -4
  20. package/dist/hooks.d.ts +24 -4
  21. package/dist/hooks.mjs +130 -105
  22. package/dist/hooks.mjs.map +1 -1
  23. package/dist/index.cjs +373 -311
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +54 -8
  26. package/dist/index.d.ts +54 -8
  27. package/dist/index.mjs +373 -311
  28. package/dist/index.mjs.map +1 -1
  29. package/package.json +3 -3
  30. package/src/auth/context/AccountsContext.tsx +3 -3
  31. package/src/auth/hooks/useTokenRefresh.ts +4 -10
  32. package/src/auth/middlewares/tokenRefresh.ts +4 -15
  33. package/src/generated/cfg_accounts/CLAUDE.md +2 -9
  34. package/src/generated/cfg_accounts/_utils/fetchers/accounts__user_profile.ts +2 -1
  35. package/src/generated/cfg_accounts/_utils/hooks/accounts__user_profile.ts +2 -1
  36. package/src/generated/cfg_accounts/_utils/schemas/CfgAccountsProfileAvatarCreateRequest.schema.ts +15 -0
  37. package/src/generated/cfg_accounts/_utils/schemas/index.ts +1 -0
  38. package/src/generated/cfg_accounts/accounts/models.ts +0 -1
  39. package/src/generated/cfg_accounts/accounts__user_profile/client.ts +4 -2
  40. package/src/generated/cfg_accounts/accounts__user_profile/models.ts +9 -1
  41. package/src/generated/cfg_accounts/client.ts +18 -0
  42. package/src/generated/cfg_accounts/index.ts +3 -1
  43. package/src/generated/cfg_accounts/schema.json +2 -2
  44. package/src/generated/cfg_centrifugo/CLAUDE.md +1 -8
  45. package/src/generated/cfg_centrifugo/client.ts +18 -0
  46. package/src/generated/cfg_centrifugo/index.ts +3 -1
  47. package/src/generated/cfg_totp/CLAUDE.md +1 -8
  48. package/src/generated/cfg_totp/client.ts +18 -0
  49. package/src/generated/cfg_totp/index.ts +3 -1
  50. package/src/generated/cfg_totp/totp/models.ts +2 -0
  51. package/src/generated/cfg_webpush/CLAUDE.md +1 -8
  52. package/src/generated/cfg_webpush/client.ts +18 -0
  53. package/src/generated/cfg_webpush/index.ts +3 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/api",
3
- "version": "2.1.88",
3
+ "version": "2.1.90",
4
4
  "description": "Auto-generated TypeScript API client with React hooks, SWR integration, and Zod validation for Django REST Framework backends",
5
5
  "keywords": [
6
6
  "django",
@@ -74,7 +74,7 @@
74
74
  "check": "tsc --noEmit"
75
75
  },
76
76
  "peerDependencies": {
77
- "@djangocfg/ui-nextjs": "^2.1.88",
77
+ "@djangocfg/ui-nextjs": "^2.1.90",
78
78
  "consola": "^3.4.2",
79
79
  "next": "^14 || ^15",
80
80
  "p-retry": "^7.0.0",
@@ -85,7 +85,7 @@
85
85
  "devDependencies": {
86
86
  "@types/node": "^24.7.2",
87
87
  "@types/react": "^19.0.0",
88
- "@djangocfg/typescript-config": "^2.1.88",
88
+ "@djangocfg/typescript-config": "^2.1.90",
89
89
  "next": "^15.0.0",
90
90
  "react": "^19.0.0",
91
91
  "tsup": "^8.5.0",
@@ -59,7 +59,7 @@ export interface AccountsContextValue {
59
59
  // Profile operations
60
60
  updateProfile: (data: UserProfileUpdateRequest) => Promise<User>;
61
61
  partialUpdateProfile: (data: PatchedUserProfileUpdateRequest) => Promise<User>;
62
- uploadAvatar: (formData: FormData) => Promise<User>;
62
+ uploadAvatar: (avatar: File | Blob) => Promise<User>;
63
63
  refreshProfile: (callerId?: string) => Promise<User | undefined>;
64
64
 
65
65
  // Authentication
@@ -165,8 +165,8 @@ export function AccountsProvider({ children }: AccountsProviderProps) {
165
165
  };
166
166
 
167
167
  // Upload avatar
168
- const uploadAvatar = async (formData: FormData): Promise<User> => {
169
- const result = await avatarMutation(formData, apiAccounts);
168
+ const uploadAvatar = async (avatar: File | Blob): Promise<User> => {
169
+ const result = await avatarMutation({ avatar }, apiAccounts);
170
170
  await refreshProfile('AccountsContext.uploadAvatar');
171
171
  return result as User;
172
172
  };
@@ -75,18 +75,12 @@ export function useTokenRefresh(options: UseTokenRefreshOptions = {}) {
75
75
  authLogger.info('Refreshing token...');
76
76
 
77
77
  try {
78
- const response = await fetch('/api/accounts/token/refresh/', {
79
- method: 'POST',
80
- headers: { 'Content-Type': 'application/json' },
81
- body: JSON.stringify({ refresh: refreshTokenValue }),
78
+ // Use generated API client with correct URL (/cfg/accounts/token/refresh/)
79
+ const result = await apiAccounts.auth.accountsTokenRefreshCreate({
80
+ refresh: refreshTokenValue,
82
81
  });
83
82
 
84
- if (!response.ok) {
85
- throw new Error(`Token refresh failed: ${response.status}`);
86
- }
87
-
88
- const data = await response.json();
89
- const newAccessToken = data.access;
83
+ const newAccessToken = result.access;
90
84
 
91
85
  if (!newAccessToken) {
92
86
  throw new Error('No access token in refresh response');
@@ -50,23 +50,12 @@ export async function refreshAccessToken(): Promise<string | null> {
50
50
  return null;
51
51
  }
52
52
 
53
- // Call the refresh endpoint
54
- const response = await fetch('/api/accounts/token/refresh/', {
55
- method: 'POST',
56
- headers: {
57
- 'Content-Type': 'application/json',
58
- },
59
- body: JSON.stringify({ refresh: refreshToken }),
53
+ // Use generated API client with correct URL (/cfg/accounts/token/refresh/)
54
+ const result = await apiAccounts.auth.accountsTokenRefreshCreate({
55
+ refresh: refreshToken,
60
56
  });
61
57
 
62
- if (!response.ok) {
63
- authLogger.error('Token refresh failed with status:', response.status);
64
- onTokenRefreshed(null);
65
- return null;
66
- }
67
-
68
- const data = await response.json();
69
- const newAccessToken = data.access;
58
+ const newAccessToken = result.access;
70
59
 
71
60
  if (!newAccessToken) {
72
61
  authLogger.error('Token refresh response missing access token');
@@ -12,7 +12,7 @@ python manage.py generate_client --groups cfg_accounts --typescript
12
12
  |---|---|
13
13
  | Version | 3.0.3 |
14
14
  | Operations | 14 |
15
- | Schemas | 19 |
15
+ | Schemas | 20 |
16
16
 
17
17
  ## Resources
18
18
 
@@ -81,12 +81,5 @@ openapi_client = OpenAPIClientConfig(
81
81
  )
82
82
  ```
83
83
 
84
- **Copy to Next.js** (if `nextjs_admin` configured):
85
- ```python
86
- nextjs_admin = NextJsAdminConfig(
87
- project_path="../frontend/apps/...",
88
- api_output_path="app/_lib/api/generated",
89
- )
90
- ```
91
-
92
84
  @see https://djangocfg.com/docs/features/api-generation
85
+
@@ -31,6 +31,7 @@
31
31
  * ```
32
32
  */
33
33
  import { consola } from 'consola'
34
+ import { CfgAccountsProfileAvatarCreateRequestSchema, type CfgAccountsProfileAvatarCreateRequest } from '../schemas/CfgAccountsProfileAvatarCreateRequest.schema'
34
35
  import { PatchedUserProfileUpdateRequestSchema, type PatchedUserProfileUpdateRequest } from '../schemas/PatchedUserProfileUpdateRequest.schema'
35
36
  import { UserSchema, type User } from '../schemas/User.schema'
36
37
  import { UserProfileUpdateRequestSchema, type UserProfileUpdateRequest } from '../schemas/UserProfileUpdateRequest.schema'
@@ -99,7 +100,7 @@ export async function getAccountsProfileRetrieve( client?: any
99
100
  * @method POST
100
101
  * @path /cfg/accounts/profile/avatar/
101
102
  */
102
- export async function createAccountsProfileAvatarCreate( data: any, client?: any
103
+ export async function createAccountsProfileAvatarCreate( data: CfgAccountsProfileAvatarCreateRequest, client?: any
103
104
  ): Promise<User> {
104
105
  const api = client || getAPIInstance()
105
106
  const response = await api.user_profile.accountsProfileAvatarCreate(data)
@@ -21,6 +21,7 @@ import useSWR from 'swr'
21
21
  import { useSWRConfig } from 'swr'
22
22
  import * as Fetchers from '../fetchers/accounts__user_profile'
23
23
  import type { API } from '../../index'
24
+ import type { CfgAccountsProfileAvatarCreateRequest } from '../schemas/CfgAccountsProfileAvatarCreateRequest.schema'
24
25
  import type { PatchedUserProfileUpdateRequest } from '../schemas/PatchedUserProfileUpdateRequest.schema'
25
26
  import type { User } from '../schemas/User.schema'
26
27
  import type { UserProfileUpdateRequest } from '../schemas/UserProfileUpdateRequest.schema'
@@ -48,7 +49,7 @@ export function useAccountsProfileRetrieve(client?: API): ReturnType<typeof useS
48
49
  export function useCreateAccountsProfileAvatarCreate() {
49
50
  const { mutate } = useSWRConfig()
50
51
 
51
- return async (data: any, client?: API): Promise<User> => {
52
+ return async (data: CfgAccountsProfileAvatarCreateRequest, client?: API): Promise<User> => {
52
53
  const result = await Fetchers.createAccountsProfileAvatarCreate(data, client)
53
54
  // Revalidate related queries
54
55
  mutate('cfg-accounts-profile-avatar')
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Zod schema for CfgAccountsProfileAvatarCreateRequest
3
+ *
4
+ * This schema provides runtime validation and type inference.
5
+ * */
6
+ import { z } from 'zod'
7
+
8
+ export const CfgAccountsProfileAvatarCreateRequestSchema = z.object({
9
+ avatar: z.union([z.instanceof(File), z.instanceof(Blob)]),
10
+ })
11
+
12
+ /**
13
+ * Infer TypeScript type from Zod schema
14
+ */
15
+ export type CfgAccountsProfileAvatarCreateRequest = z.infer<typeof CfgAccountsProfileAvatarCreateRequestSchema>
@@ -18,6 +18,7 @@
18
18
  */
19
19
 
20
20
  export * from './CentrifugoToken.schema'
21
+ export * from './CfgAccountsProfileAvatarCreateRequest.schema'
21
22
  export * from './OAuthAuthorizeRequestRequest.schema'
22
23
  export * from './OAuthAuthorizeResponse.schema'
23
24
  export * from './OAuthCallbackRequestRequest.schema'
@@ -104,7 +104,6 @@ export interface User {
104
104
  is_superuser: boolean;
105
105
  date_joined: string;
106
106
  last_login?: string | null;
107
- /** Get count of unanswered messages for the user. */
108
107
  unanswered_messages_count: number;
109
108
  centrifugo: CentrifugoToken | null;
110
109
  }
@@ -27,8 +27,10 @@ export class UserProfile {
27
27
  * Upload avatar image for the current authenticated user. Accepts
28
28
  * multipart/form-data with 'avatar' field.
29
29
  */
30
- async accountsProfileAvatarCreate(data: FormData): Promise<Models.User> {
31
- const response = await this.client.request('POST', "/cfg/accounts/profile/avatar/", { formData: data });
30
+ async accountsProfileAvatarCreate(data: Models.CfgAccountsProfileAvatarCreateRequest): Promise<Models.User> {
31
+ const formData = new FormData();
32
+ formData.append('avatar', data.avatar);
33
+ const response = await this.client.request('POST', "/cfg/accounts/profile/avatar/", { formData });
32
34
  return response;
33
35
  }
34
36
 
@@ -25,11 +25,19 @@ export interface User {
25
25
  is_superuser: boolean;
26
26
  date_joined: string;
27
27
  last_login?: string | null;
28
- /** Get count of unanswered messages for the user. */
29
28
  unanswered_messages_count: number;
30
29
  centrifugo: CentrifugoToken | null;
31
30
  }
32
31
 
32
+ /**
33
+ *
34
+ * Request model (no read-only fields).
35
+ */
36
+ export interface CfgAccountsProfileAvatarCreateRequest {
37
+ /** Avatar image file (JPEG, PNG, GIF, WebP, max 5MB) */
38
+ avatar: File | Blob;
39
+ }
40
+
33
41
  /**
34
42
  * Serializer for updating user profile.
35
43
  *
@@ -28,6 +28,7 @@ export class APIClient {
28
28
  private httpClient: HttpClientAdapter;
29
29
  private logger: APILogger | null = null;
30
30
  private retryConfig: RetryConfig | null = null;
31
+ private tokenGetter: (() => string | null) | null = null;
31
32
 
32
33
  // Sub-clients
33
34
  public auth: Auth;
@@ -41,10 +42,12 @@ export class APIClient {
41
42
  httpClient?: HttpClientAdapter;
42
43
  loggerConfig?: Partial<LoggerConfig>;
43
44
  retryConfig?: RetryConfig;
45
+ tokenGetter?: () => string | null;
44
46
  }
45
47
  ) {
46
48
  this.baseUrl = baseUrl.replace(/\/$/, '');
47
49
  this.httpClient = options?.httpClient || new FetchAdapter();
50
+ this.tokenGetter = options?.tokenGetter || null;
48
51
 
49
52
  // Initialize logger if config provided
50
53
  if (options?.loggerConfig !== undefined) {
@@ -78,6 +81,21 @@ export class APIClient {
78
81
  return null;
79
82
  }
80
83
 
84
+ /**
85
+ * Get the base URL for building streaming/download URLs.
86
+ */
87
+ getBaseUrl(): string {
88
+ return this.baseUrl;
89
+ }
90
+
91
+ /**
92
+ * Get JWT token for URL authentication (used in streaming endpoints).
93
+ * Returns null if no token getter is configured or no token is available.
94
+ */
95
+ getToken(): string | null {
96
+ return this.tokenGetter ? this.tokenGetter() : null;
97
+ }
98
+
81
99
  /**
82
100
  * Make HTTP request with Django CSRF and session handling.
83
101
  * Automatically retries on network errors and 5xx server errors.
@@ -148,10 +148,11 @@ export class API {
148
148
 
149
149
  this._loadTokensFromStorage();
150
150
 
151
- // Initialize APIClient
151
+ // Initialize APIClient with token getter for URL authentication
152
152
  this._client = new APIClient(this.baseUrl, {
153
153
  retryConfig: this.options?.retryConfig,
154
154
  loggerConfig: this.options?.loggerConfig,
155
+ tokenGetter: () => this.getToken(),
155
156
  });
156
157
 
157
158
  // Always inject auth header wrapper (reads token dynamically from storage)
@@ -173,6 +174,7 @@ export class API {
173
174
  this._client = new APIClient(this.baseUrl, {
174
175
  retryConfig: this.options?.retryConfig,
175
176
  loggerConfig: this.options?.loggerConfig,
177
+ tokenGetter: () => this.getToken(),
176
178
  });
177
179
 
178
180
  // Always inject auth header wrapper (reads token dynamically from storage)
@@ -1386,8 +1386,8 @@
1386
1386
  },
1387
1387
  "unanswered_messages_count": {
1388
1388
  "type": "integer",
1389
- "description": "Get count of unanswered messages for the user.",
1390
- "readOnly": true
1389
+ "readOnly": true,
1390
+ "default": 0
1391
1391
  },
1392
1392
  "centrifugo": {
1393
1393
  "allOf": [
@@ -81,12 +81,5 @@ openapi_client = OpenAPIClientConfig(
81
81
  )
82
82
  ```
83
83
 
84
- **Copy to Next.js** (if `nextjs_admin` configured):
85
- ```python
86
- nextjs_admin = NextJsAdminConfig(
87
- project_path="../frontend/apps/...",
88
- api_output_path="app/_lib/api/generated",
89
- )
90
- ```
91
-
92
84
  @see https://djangocfg.com/docs/features/api-generation
85
+
@@ -28,6 +28,7 @@ export class APIClient {
28
28
  private httpClient: HttpClientAdapter;
29
29
  private logger: APILogger | null = null;
30
30
  private retryConfig: RetryConfig | null = null;
31
+ private tokenGetter: (() => string | null) | null = null;
31
32
 
32
33
  // Sub-clients
33
34
  public centrifugo_admin_api: CentrifugoAdminApi;
@@ -41,10 +42,12 @@ export class APIClient {
41
42
  httpClient?: HttpClientAdapter;
42
43
  loggerConfig?: Partial<LoggerConfig>;
43
44
  retryConfig?: RetryConfig;
45
+ tokenGetter?: () => string | null;
44
46
  }
45
47
  ) {
46
48
  this.baseUrl = baseUrl.replace(/\/$/, '');
47
49
  this.httpClient = options?.httpClient || new FetchAdapter();
50
+ this.tokenGetter = options?.tokenGetter || null;
48
51
 
49
52
  // Initialize logger if config provided
50
53
  if (options?.loggerConfig !== undefined) {
@@ -78,6 +81,21 @@ export class APIClient {
78
81
  return null;
79
82
  }
80
83
 
84
+ /**
85
+ * Get the base URL for building streaming/download URLs.
86
+ */
87
+ getBaseUrl(): string {
88
+ return this.baseUrl;
89
+ }
90
+
91
+ /**
92
+ * Get JWT token for URL authentication (used in streaming endpoints).
93
+ * Returns null if no token getter is configured or no token is available.
94
+ */
95
+ getToken(): string | null {
96
+ return this.tokenGetter ? this.tokenGetter() : null;
97
+ }
98
+
81
99
  /**
82
100
  * Make HTTP request with Django CSRF and session handling.
83
101
  * Automatically retries on network errors and 5xx server errors.
@@ -147,10 +147,11 @@ export class API {
147
147
 
148
148
  this._loadTokensFromStorage();
149
149
 
150
- // Initialize APIClient
150
+ // Initialize APIClient with token getter for URL authentication
151
151
  this._client = new APIClient(this.baseUrl, {
152
152
  retryConfig: this.options?.retryConfig,
153
153
  loggerConfig: this.options?.loggerConfig,
154
+ tokenGetter: () => this.getToken(),
154
155
  });
155
156
 
156
157
  // Always inject auth header wrapper (reads token dynamically from storage)
@@ -172,6 +173,7 @@ export class API {
172
173
  this._client = new APIClient(this.baseUrl, {
173
174
  retryConfig: this.options?.retryConfig,
174
175
  loggerConfig: this.options?.loggerConfig,
176
+ tokenGetter: () => this.getToken(),
175
177
  });
176
178
 
177
179
  // Always inject auth header wrapper (reads token dynamically from storage)
@@ -79,12 +79,5 @@ openapi_client = OpenAPIClientConfig(
79
79
  )
80
80
  ```
81
81
 
82
- **Copy to Next.js** (if `nextjs_admin` configured):
83
- ```python
84
- nextjs_admin = NextJsAdminConfig(
85
- project_path="../frontend/apps/...",
86
- api_output_path="app/_lib/api/generated",
87
- )
88
- ```
89
-
90
82
  @see https://djangocfg.com/docs/features/api-generation
83
+
@@ -29,6 +29,7 @@ export class APIClient {
29
29
  private httpClient: HttpClientAdapter;
30
30
  private logger: APILogger | null = null;
31
31
  private retryConfig: RetryConfig | null = null;
32
+ private tokenGetter: (() => string | null) | null = null;
32
33
 
33
34
  // Sub-clients
34
35
  public backup_codes: BackupCodes;
@@ -43,10 +44,12 @@ export class APIClient {
43
44
  httpClient?: HttpClientAdapter;
44
45
  loggerConfig?: Partial<LoggerConfig>;
45
46
  retryConfig?: RetryConfig;
47
+ tokenGetter?: () => string | null;
46
48
  }
47
49
  ) {
48
50
  this.baseUrl = baseUrl.replace(/\/$/, '');
49
51
  this.httpClient = options?.httpClient || new FetchAdapter();
52
+ this.tokenGetter = options?.tokenGetter || null;
50
53
 
51
54
  // Initialize logger if config provided
52
55
  if (options?.loggerConfig !== undefined) {
@@ -81,6 +84,21 @@ export class APIClient {
81
84
  return null;
82
85
  }
83
86
 
87
+ /**
88
+ * Get the base URL for building streaming/download URLs.
89
+ */
90
+ getBaseUrl(): string {
91
+ return this.baseUrl;
92
+ }
93
+
94
+ /**
95
+ * Get JWT token for URL authentication (used in streaming endpoints).
96
+ * Returns null if no token getter is configured or no token is available.
97
+ */
98
+ getToken(): string | null {
99
+ return this.tokenGetter ? this.tokenGetter() : null;
100
+ }
101
+
84
102
  /**
85
103
  * Make HTTP request with Django CSRF and session handling.
86
104
  * Automatically retries on network errors and 5xx server errors.
@@ -153,10 +153,11 @@ export class API {
153
153
 
154
154
  this._loadTokensFromStorage();
155
155
 
156
- // Initialize APIClient
156
+ // Initialize APIClient with token getter for URL authentication
157
157
  this._client = new APIClient(this.baseUrl, {
158
158
  retryConfig: this.options?.retryConfig,
159
159
  loggerConfig: this.options?.loggerConfig,
160
+ tokenGetter: () => this.getToken(),
160
161
  });
161
162
 
162
163
  // Always inject auth header wrapper (reads token dynamically from storage)
@@ -179,6 +180,7 @@ export class API {
179
180
  this._client = new APIClient(this.baseUrl, {
180
181
  retryConfig: this.options?.retryConfig,
181
182
  loggerConfig: this.options?.loggerConfig,
183
+ tokenGetter: () => this.getToken(),
182
184
  });
183
185
 
184
186
  // Always inject auth header wrapper (reads token dynamically from storage)
@@ -1 +1,3 @@
1
1
  // Auto-generated by DjangoCFG - see CLAUDE.md
2
+ // No models for this endpoint (all types are primitives or binary)
3
+ export {};
@@ -59,12 +59,5 @@ openapi_client = OpenAPIClientConfig(
59
59
  )
60
60
  ```
61
61
 
62
- **Copy to Next.js** (if `nextjs_admin` configured):
63
- ```python
64
- nextjs_admin = NextJsAdminConfig(
65
- project_path="../frontend/apps/...",
66
- api_output_path="app/_lib/api/generated",
67
- )
68
- ```
69
-
70
62
  @see https://djangocfg.com/docs/features/api-generation
63
+
@@ -25,6 +25,7 @@ export class APIClient {
25
25
  private httpClient: HttpClientAdapter;
26
26
  private logger: APILogger | null = null;
27
27
  private retryConfig: RetryConfig | null = null;
28
+ private tokenGetter: (() => string | null) | null = null;
28
29
 
29
30
  // Sub-clients
30
31
  public web_push: WebPush;
@@ -35,10 +36,12 @@ export class APIClient {
35
36
  httpClient?: HttpClientAdapter;
36
37
  loggerConfig?: Partial<LoggerConfig>;
37
38
  retryConfig?: RetryConfig;
39
+ tokenGetter?: () => string | null;
38
40
  }
39
41
  ) {
40
42
  this.baseUrl = baseUrl.replace(/\/$/, '');
41
43
  this.httpClient = options?.httpClient || new FetchAdapter();
44
+ this.tokenGetter = options?.tokenGetter || null;
42
45
 
43
46
  // Initialize logger if config provided
44
47
  if (options?.loggerConfig !== undefined) {
@@ -69,6 +72,21 @@ export class APIClient {
69
72
  return null;
70
73
  }
71
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
+
72
90
  /**
73
91
  * Make HTTP request with Django CSRF and session handling.
74
92
  * Automatically retries on network errors and 5xx server errors.
@@ -132,10 +132,11 @@ export class API {
132
132
 
133
133
  this._loadTokensFromStorage();
134
134
 
135
- // Initialize APIClient
135
+ // Initialize APIClient with token getter for URL authentication
136
136
  this._client = new APIClient(this.baseUrl, {
137
137
  retryConfig: this.options?.retryConfig,
138
138
  loggerConfig: this.options?.loggerConfig,
139
+ tokenGetter: () => this.getToken(),
139
140
  });
140
141
 
141
142
  // Always inject auth header wrapper (reads token dynamically from storage)
@@ -154,6 +155,7 @@ export class API {
154
155
  this._client = new APIClient(this.baseUrl, {
155
156
  retryConfig: this.options?.retryConfig,
156
157
  loggerConfig: this.options?.loggerConfig,
158
+ tokenGetter: () => this.getToken(),
157
159
  });
158
160
 
159
161
  // Always inject auth header wrapper (reads token dynamically from storage)