@djangocfg/api 2.1.333 → 2.1.335

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/dist/index.cjs CHANGED
@@ -145,6 +145,9 @@ var _apiKeyOverride = null;
145
145
  var _baseUrlOverride = null;
146
146
  var _withCredentials = true;
147
147
  var _onUnauthorized = null;
148
+ var _refreshHandler = null;
149
+ var _refreshInflight = null;
150
+ var RETRY_MARKER = "X-Auth-Retry";
148
151
  var _client = null;
149
152
  function pushClientConfig() {
150
153
  if (!_client) return;
@@ -223,10 +226,53 @@ var auth = {
223
226
  pushClientConfig();
224
227
  },
225
228
  // ── 401 handler ───────────────────────────────────────────────────
229
+ /**
230
+ * Fired when the server returns 401 AND no refresh path recovers it
231
+ * (no refresh token, no refresh handler, refresh failed, or retry
232
+ * still 401). The app should clear local state and redirect to login.
233
+ *
234
+ * NOT fired for 401 that gets transparently recovered by the refresh
235
+ * handler — those are invisible to callers.
236
+ */
226
237
  onUnauthorized(cb) {
227
238
  _onUnauthorized = cb;
239
+ },
240
+ /**
241
+ * Register the refresh strategy. The handler receives the current
242
+ * refresh token and must call your refresh endpoint, returning
243
+ * `{ access, refresh? }` on success or `null` on failure.
244
+ *
245
+ * @example
246
+ * auth.setRefreshHandler(async (refresh) => {
247
+ * const { data } = await Auth.tokenRefreshCreate({ body: { refresh } });
248
+ * return data ? { access: data.access, refresh: data.refresh } : null;
249
+ * });
250
+ */
251
+ setRefreshHandler(fn) {
252
+ _refreshHandler = fn;
228
253
  }
229
254
  };
255
+ async function tryRefresh() {
256
+ if (_refreshInflight) return _refreshInflight;
257
+ if (!_refreshHandler) return null;
258
+ const refresh = auth.getRefreshToken();
259
+ if (!refresh) return null;
260
+ _refreshInflight = (async () => {
261
+ try {
262
+ const result = await _refreshHandler(refresh);
263
+ if (!result?.access) return null;
264
+ auth.setToken(result.access);
265
+ if (result.refresh) auth.setRefreshToken(result.refresh);
266
+ return result.access;
267
+ } catch {
268
+ return null;
269
+ } finally {
270
+ _refreshInflight = null;
271
+ }
272
+ })();
273
+ return _refreshInflight;
274
+ }
275
+ __name(tryRefresh, "tryRefresh");
230
276
  function installAuthOnClient(client2) {
231
277
  if (_client) return;
232
278
  _client = client2;
@@ -243,14 +289,48 @@ function installAuthOnClient(client2) {
243
289
  if (apiKey) request.headers.set("X-API-Key", apiKey);
244
290
  return request;
245
291
  });
246
- client2.interceptors.response.use((response) => {
247
- if (response.status === 401 && _onUnauthorized) {
248
- try {
249
- _onUnauthorized(response);
250
- } catch {
292
+ client2.interceptors.response.use(async (response, request) => {
293
+ if (response.status !== 401) return response;
294
+ if (request.headers.get(RETRY_MARKER)) {
295
+ if (_onUnauthorized) {
296
+ try {
297
+ _onUnauthorized(response);
298
+ } catch {
299
+ }
300
+ }
301
+ return response;
302
+ }
303
+ const newToken = await tryRefresh();
304
+ if (!newToken) {
305
+ if (_onUnauthorized) {
306
+ try {
307
+ _onUnauthorized(response);
308
+ } catch {
309
+ }
310
+ }
311
+ return response;
312
+ }
313
+ const retry = request.clone();
314
+ retry.headers.set("Authorization", `Bearer ${newToken}`);
315
+ retry.headers.set(RETRY_MARKER, "1");
316
+ try {
317
+ const retried = await fetch(retry);
318
+ if (retried.status === 401 && _onUnauthorized) {
319
+ try {
320
+ _onUnauthorized(retried);
321
+ } catch {
322
+ }
323
+ }
324
+ return retried;
325
+ } catch {
326
+ if (_onUnauthorized) {
327
+ try {
328
+ _onUnauthorized(response);
329
+ } catch {
330
+ }
251
331
  }
332
+ return response;
252
333
  }
253
- return response;
254
334
  });
255
335
  }
256
336
  __name(installAuthOnClient, "installAuthOnClient");
@@ -401,6 +481,15 @@ var API = class {
401
481
  setApiKey(key) {
402
482
  auth.setApiKey(key);
403
483
  }
484
+ // ── 401 handling ────────────────────────────────────────────────────────
485
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
486
+ onUnauthorized(cb) {
487
+ auth.onUnauthorized(cb);
488
+ }
489
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
490
+ setRefreshHandler(fn) {
491
+ auth.setRefreshHandler(fn);
492
+ }
404
493
  };
405
494
 
406
495
  // src/_api/generated/helpers/storage.ts
@@ -653,6 +742,15 @@ var API2 = class {
653
742
  setApiKey(key) {
654
743
  auth.setApiKey(key);
655
744
  }
745
+ // ── 401 handling ────────────────────────────────────────────────────────
746
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
747
+ onUnauthorized(cb) {
748
+ auth.onUnauthorized(cb);
749
+ }
750
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
751
+ setRefreshHandler(fn) {
752
+ auth.setRefreshHandler(fn);
753
+ }
656
754
  };
657
755
 
658
756
  // src/_api/generated/_cfg_totp/api.ts
@@ -707,6 +805,15 @@ var API3 = class {
707
805
  setApiKey(key) {
708
806
  auth.setApiKey(key);
709
807
  }
808
+ // ── 401 handling ────────────────────────────────────────────────────────
809
+ /** Fired only on terminal 401 (after refresh+retry path is exhausted). */
810
+ onUnauthorized(cb) {
811
+ auth.onUnauthorized(cb);
812
+ }
813
+ /** Provide a refresh strategy. See `auth.setRefreshHandler` for the contract. */
814
+ setRefreshHandler(fn) {
815
+ auth.setRefreshHandler(fn);
816
+ }
710
817
  };
711
818
 
712
819
  // src/_api/generated/index.ts