@djangocfg/api 2.1.332 → 2.1.333

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/api",
3
- "version": "2.1.332",
3
+ "version": "2.1.333",
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",
@@ -79,7 +79,7 @@
79
79
  "devDependencies": {
80
80
  "@types/node": "^24.7.2",
81
81
  "@types/react": "^19.1.0",
82
- "@djangocfg/typescript-config": "^2.1.332",
82
+ "@djangocfg/typescript-config": "^2.1.333",
83
83
  "next": "^16.2.2",
84
84
  "react": "^19.1.0",
85
85
  "tsup": "^8.5.0",
@@ -15,5 +15,6 @@ export type CreateClientConfig<T extends ClientOptions = ClientOptions2> = (over
15
15
 
16
16
  export const client = createClient(createConfig<ClientOptions2>({ baseUrl: 'http://localhost:8000' }));
17
17
 
18
- // auto-init: load auth interceptor
19
- import './helpers/auth';
18
+ // auto-init: install auth on client
19
+ import { installAuthOnClient } from './helpers/auth';
20
+ installAuthOnClient(client);
@@ -1,10 +1,9 @@
1
1
  // AUTO-GENERATED by django_generator / ts_extras.wrapper
2
- // Global auth store. ONE source of truth for token / API key / locale /
3
- // baseUrl. Installs the request interceptor as a side-effect on import.
2
+ // Global auth store. Wired into the shared `client` from `client.gen.ts`
3
+ // via `installAuthOnClient(client)` called synchronously by the
4
+ // post-processed bottom of client.gen.ts. No circular import here.
4
5
  // DO NOT EDIT — re-run `make gen`.
5
6
 
6
- import { client } from '../client.gen';
7
-
8
7
  const ACCESS_KEY = 'cfg.access_token';
9
8
  const REFRESH_KEY = 'cfg.refresh_token';
10
9
  const API_KEY_KEY = 'cfg.api_key';
@@ -106,6 +105,30 @@ let _baseUrlOverride: string | null = null;
106
105
  let _withCredentials = true;
107
106
  let _onUnauthorized: ((response: Response) => void) | null = null;
108
107
 
108
+ /**
109
+ * Captured reference to the shared Hey API client. Set exactly once by
110
+ * `installAuthOnClient(client)` (called from client.gen.ts). All `auth.set*`
111
+ * methods that mutate transport config (baseUrl / credentials) push through
112
+ * this reference. Until installed, those mutations are silently buffered as
113
+ * in-memory state — the next request after install will pick them up.
114
+ */
115
+ type HeyClient = {
116
+ setConfig(opts: Record<string, unknown>): void;
117
+ interceptors: {
118
+ request: { use(fn: (req: Request) => Request | Promise<Request>): void };
119
+ response: { use(fn: (res: Response, req: Request) => Response | Promise<Response>): void };
120
+ };
121
+ };
122
+ let _client: HeyClient | null = null;
123
+
124
+ function pushClientConfig(): void {
125
+ if (!_client) return;
126
+ _client.setConfig({
127
+ baseUrl: auth.getBaseUrl(),
128
+ credentials: _withCredentials ? 'include' : 'same-origin',
129
+ });
130
+ }
131
+
109
132
  /**
110
133
  * Global auth/config store. All getters read live state every call —
111
134
  * the interceptor below uses these to attach headers per-request.
@@ -116,24 +139,13 @@ let _onUnauthorized: ((response: Response) => void) | null = null;
116
139
  *
117
140
  * @example
118
141
  * import { auth } from '@your/api';
119
- *
120
- * // After login
121
142
  * auth.setToken(jwt);
122
- * auth.setRefreshToken(refresh);
123
- *
124
- * // After logout
125
143
  * auth.clearTokens();
126
- *
127
- * // Switch to cookie storage (call once during app init)
128
144
  * auth.setStorageMode('cookie');
129
145
  */
130
146
  export const auth = {
131
147
  // ── Storage mode ──────────────────────────────────────────────────
132
148
  getStorageMode(): StorageMode { return _storageMode; },
133
- /**
134
- * Switch the storage backend. Existing values in the *previous*
135
- * backend are NOT migrated — set fresh values after switching.
136
- */
137
149
  setStorageMode(mode: StorageMode): void {
138
150
  _storageMode = mode;
139
151
  _storage = mode === 'cookie' ? cookieBackend : localStorageBackend;
@@ -148,13 +160,10 @@ export const auth = {
148
160
  isAuthenticated(): boolean { return _storage.get(ACCESS_KEY) !== null; },
149
161
 
150
162
  // ── API key ───────────────────────────────────────────────────────
151
- /** In-memory API key. Falls back to storage, then NEXT_PUBLIC_API_KEY. */
152
163
  getApiKey(): string | null {
153
164
  return _apiKeyOverride ?? _storage.get(API_KEY_KEY) ?? defaultApiKey();
154
165
  },
155
- /** In-memory only (cleared on reload). */
156
166
  setApiKey(key: string | null): void { _apiKeyOverride = key; },
157
- /** Persist to active storage backend (localStorage or cookie). */
158
167
  setApiKeyPersist(key: string | null): void {
159
168
  _apiKeyOverride = key;
160
169
  _storage.set(API_KEY_KEY, key);
@@ -162,7 +171,6 @@ export const auth = {
162
171
  clearApiKey(): void { _apiKeyOverride = null; _storage.set(API_KEY_KEY, null); },
163
172
 
164
173
  // ── Locale ────────────────────────────────────────────────────────
165
- /** Override locale → falls back to NEXT_LOCALE cookie / navigator.language. */
166
174
  getLocale(): string | null { return _localeOverride ?? detectLocale(); },
167
175
  setLocale(locale: string | null): void { _localeOverride = locale; },
168
176
 
@@ -173,51 +181,60 @@ export const auth = {
173
181
  },
174
182
  setBaseUrl(url: string | null): void {
175
183
  _baseUrlOverride = url ? url.replace(/\/$/, '') : null;
176
- client.setConfig({ baseUrl: this.getBaseUrl() });
184
+ pushClientConfig();
177
185
  },
178
186
 
179
- // ── Credentials toggle (Django session/CSRF cross-origin) ─────────
187
+ // ── Credentials toggle ────────────────────────────────────────────
180
188
  getWithCredentials(): boolean { return _withCredentials; },
181
189
  setWithCredentials(value: boolean): void {
182
190
  _withCredentials = value;
183
- client.setConfig({ credentials: value ? 'include' : 'same-origin' });
191
+ pushClientConfig();
184
192
  },
185
193
 
186
194
  // ── 401 handler ───────────────────────────────────────────────────
187
- /**
188
- * Register a callback fired on every 401 response. Use this to wire
189
- * a token-refresh flow or a forced logout. Setting `null` removes
190
- * the handler.
191
- */
192
195
  onUnauthorized(cb: ((response: Response) => void) | null): void {
193
196
  _onUnauthorized = cb;
194
197
  },
195
198
  };
196
199
 
197
- // ── One-time client wiring (side-effect on first import) ───────────────────
198
- client.setConfig({
199
- baseUrl: auth.getBaseUrl(),
200
- credentials: _withCredentials ? 'include' : 'same-origin',
201
- });
200
+ /**
201
+ * Wire the shared client to the global auth store. Called exactly
202
+ * once from `client.gen.ts` (post-processed) right after
203
+ * `createClient()`. Synchronous no microtask, no TDZ races.
204
+ *
205
+ * Safe to call from server / SSR: storage backends short-circuit on
206
+ * non-browser environments, so headers populated by the interceptor
207
+ * are simply absent server-side (which is the correct behaviour
208
+ * unless the caller explicitly sets a server-side token).
209
+ */
210
+ export function installAuthOnClient(client: HeyClient): void {
211
+ if (_client) return; // idempotent
212
+ _client = client;
202
213
 
203
- client.interceptors.request.use((request) => {
204
- const token = auth.getToken();
205
- if (token) request.headers.set('Authorization', `Bearer ${token}`);
214
+ client.setConfig({
215
+ baseUrl: auth.getBaseUrl(),
216
+ credentials: _withCredentials ? 'include' : 'same-origin',
217
+ });
206
218
 
207
- const locale = auth.getLocale();
208
- if (locale) request.headers.set('Accept-Language', locale);
219
+ client.interceptors.request.use((request) => {
220
+ const token = auth.getToken();
221
+ if (token) request.headers.set('Authorization', `Bearer ${token}`);
209
222
 
210
- const apiKey = auth.getApiKey();
211
- if (apiKey) request.headers.set('X-API-Key', apiKey);
223
+ const locale = auth.getLocale();
224
+ if (locale) request.headers.set('Accept-Language', locale);
212
225
 
213
- return request;
214
- });
226
+ const apiKey = auth.getApiKey();
227
+ if (apiKey) request.headers.set('X-API-Key', apiKey);
215
228
 
216
- client.interceptors.response.use((response) => {
217
- if (response.status === 401 && _onUnauthorized) {
218
- try { _onUnauthorized(response); } catch {}
219
- }
220
- return response;
221
- });
229
+ return request;
230
+ });
231
+
232
+ client.interceptors.response.use((response) => {
233
+ if (response.status === 401 && _onUnauthorized) {
234
+ try { _onUnauthorized(response); } catch {}
235
+ }
236
+ return response;
237
+ });
238
+ }
222
239
 
223
240
  export type Auth = typeof auth;