@djangocfg/api 2.1.456 → 2.1.457

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/auth.mjs CHANGED
@@ -1,16 +1,6 @@
1
1
  "use client";
2
- import {
3
- APIError,
4
- CfgAccounts,
5
- CfgAccountsAuth,
6
- CfgAccountsOauth,
7
- CfgAccountsProfile,
8
- CfgTotp,
9
- CfgTotpSetup,
10
- CfgTotpVerify,
11
- __name,
12
- auth
13
- } from "./chunk-4BPRCONN.mjs";
2
+ var __defProp = Object.defineProperty;
3
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
14
4
 
15
5
  // src/auth/constants.ts
16
6
  var AUTH_CONSTANTS = {
@@ -21,8 +11,6 @@ var AUTH_CONSTANTS = {
21
11
  /** Backup code max length. */
22
12
  BACKUP_CODE_MAX_LENGTH: 12
23
13
  };
24
- var DEFAULT_AUTH_PATH = "/auth";
25
- var DEFAULT_CALLBACK_PATH = "/dashboard";
26
14
 
27
15
  // src/auth/context/AuthContext.tsx
28
16
  import { usePathname as usePathname3 } from "next/navigation";
@@ -37,6 +25,653 @@ import {
37
25
  useState as useState11
38
26
  } from "react";
39
27
 
28
+ // src/_api/generated/helpers/errors.ts
29
+ var APIError = class extends Error {
30
+ constructor(statusCode, statusText, response, url, message) {
31
+ super(message || `HTTP ${statusCode}: ${statusText}`);
32
+ this.statusCode = statusCode;
33
+ this.statusText = statusText;
34
+ this.response = response;
35
+ this.url = url;
36
+ this.name = "APIError";
37
+ }
38
+ static {
39
+ __name(this, "APIError");
40
+ }
41
+ get details() {
42
+ if (typeof this.response === "object" && this.response !== null) {
43
+ return this.response;
44
+ }
45
+ return null;
46
+ }
47
+ get fieldErrors() {
48
+ const details = this.details;
49
+ if (!details) return null;
50
+ const fieldErrors = {};
51
+ for (const [key, value] of Object.entries(details)) {
52
+ if (Array.isArray(value)) fieldErrors[key] = value;
53
+ }
54
+ return Object.keys(fieldErrors).length > 0 ? fieldErrors : null;
55
+ }
56
+ get errorMessage() {
57
+ const details = this.details;
58
+ if (!details) return this.message;
59
+ if (details.detail) {
60
+ return Array.isArray(details.detail) ? details.detail.join(", ") : String(details.detail);
61
+ }
62
+ if (details.error) return String(details.error);
63
+ if (details.message) return String(details.message);
64
+ const fieldErrors = this.fieldErrors;
65
+ if (fieldErrors) {
66
+ const firstField = Object.keys(fieldErrors)[0];
67
+ if (firstField) return `${firstField}: ${fieldErrors[firstField]?.join(", ")}`;
68
+ }
69
+ return this.message;
70
+ }
71
+ get isValidationError() {
72
+ return this.statusCode === 400;
73
+ }
74
+ get isAuthError() {
75
+ return this.statusCode === 401;
76
+ }
77
+ get isPermissionError() {
78
+ return this.statusCode === 403;
79
+ }
80
+ get isNotFoundError() {
81
+ return this.statusCode === 404;
82
+ }
83
+ get isServerError() {
84
+ return this.statusCode >= 500 && this.statusCode < 600;
85
+ }
86
+ };
87
+
88
+ // src/_api/generated/helpers/auth.ts
89
+ var ACCESS_KEY = "cfg.access_token";
90
+ var REFRESH_KEY = "cfg.refresh_token";
91
+ var API_KEY_KEY = "cfg.api_key";
92
+ var isBrowser = typeof window !== "undefined";
93
+ var localStorageBackend = {
94
+ get(key) {
95
+ if (!isBrowser) return null;
96
+ try {
97
+ return window.localStorage.getItem(key);
98
+ } catch {
99
+ return null;
100
+ }
101
+ },
102
+ set(key, value) {
103
+ if (!isBrowser) return;
104
+ try {
105
+ if (value === null) window.localStorage.removeItem(key);
106
+ else window.localStorage.setItem(key, value);
107
+ } catch {
108
+ }
109
+ }
110
+ };
111
+ var COOKIE_MAX_AGE = 60 * 60 * 24 * 30;
112
+ var cookieBackend = {
113
+ get(key) {
114
+ if (!isBrowser) return null;
115
+ try {
116
+ const re = new RegExp(`(?:^|;\\s*)${encodeURIComponent(key)}=([^;]*)`);
117
+ const m = document.cookie.match(re);
118
+ return m ? decodeURIComponent(m[1]) : null;
119
+ } catch {
120
+ return null;
121
+ }
122
+ },
123
+ set(key, value) {
124
+ if (!isBrowser) return;
125
+ try {
126
+ const k = encodeURIComponent(key);
127
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
128
+ if (value === null) {
129
+ document.cookie = `${k}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
130
+ } else {
131
+ const v = encodeURIComponent(value);
132
+ document.cookie = `${k}=${v}; Path=/; Max-Age=${COOKIE_MAX_AGE}; SameSite=Lax${secure}`;
133
+ }
134
+ } catch {
135
+ }
136
+ }
137
+ };
138
+ var _storage = localStorageBackend;
139
+ var _storageMode = "localStorage";
140
+ function detectLocale() {
141
+ try {
142
+ if (typeof document !== "undefined") {
143
+ const m = document.cookie.match(/(?:^|;\s*)NEXT_LOCALE=([^;]*)/);
144
+ if (m) return decodeURIComponent(m[1]);
145
+ }
146
+ if (typeof navigator !== "undefined" && navigator.language) {
147
+ return navigator.language;
148
+ }
149
+ } catch {
150
+ }
151
+ return null;
152
+ }
153
+ __name(detectLocale, "detectLocale");
154
+ function defaultBaseUrl() {
155
+ if (typeof window !== "undefined") {
156
+ try {
157
+ if (typeof process !== "undefined" && process.env) {
158
+ if (process.env.NEXT_PUBLIC_STATIC_BUILD === "true") return "";
159
+ if (process.env.NEXT_PUBLIC_API_PROXY_URL !== void 0)
160
+ return process.env.NEXT_PUBLIC_API_PROXY_URL;
161
+ return process.env.NEXT_PUBLIC_API_URL || "";
162
+ }
163
+ } catch {
164
+ }
165
+ return "";
166
+ }
167
+ try {
168
+ if (typeof process !== "undefined" && process.env) {
169
+ if (process.env.NEXT_PUBLIC_STATIC_BUILD === "true") return "";
170
+ return process.env.NEXT_PUBLIC_API_URL || "";
171
+ }
172
+ } catch {
173
+ }
174
+ return "";
175
+ }
176
+ __name(defaultBaseUrl, "defaultBaseUrl");
177
+ function defaultApiKey() {
178
+ try {
179
+ if (typeof process !== "undefined" && process.env?.NEXT_PUBLIC_API_KEY) {
180
+ return process.env.NEXT_PUBLIC_API_KEY;
181
+ }
182
+ } catch {
183
+ }
184
+ return null;
185
+ }
186
+ __name(defaultApiKey, "defaultApiKey");
187
+ var _localeOverride = null;
188
+ var _apiKeyOverride = null;
189
+ var _baseUrlOverride = null;
190
+ var _withCredentials = true;
191
+ var _onUnauthorized = null;
192
+ var _refreshHandler = null;
193
+ var _refreshInflight = null;
194
+ var RETRY_MARKER = "X-Auth-Retry";
195
+ function jwtExpMs(token) {
196
+ try {
197
+ const payload = token.split(".")[1];
198
+ if (!payload) return null;
199
+ const json = JSON.parse(atob(payload.replace(/-/g, "+").replace(/_/g, "/")));
200
+ return typeof json.exp === "number" ? json.exp * 1e3 : null;
201
+ } catch {
202
+ return null;
203
+ }
204
+ }
205
+ __name(jwtExpMs, "jwtExpMs");
206
+ function computeSnapshot() {
207
+ const access = _storage.get(ACCESS_KEY);
208
+ const refresh = _storage.get(REFRESH_KEY);
209
+ const now = Date.now();
210
+ const accessExp = access ? jwtExpMs(access) : null;
211
+ const accessAlive = access !== null && (accessExp === null || accessExp > now);
212
+ const refreshExp = refresh ? jwtExpMs(refresh) : null;
213
+ const refreshAlive = refresh !== null && (refreshExp === null || refreshExp > now);
214
+ return {
215
+ status: accessAlive || refreshAlive ? "authenticated" : "anonymous",
216
+ accessExpiresAt: accessExp
217
+ };
218
+ }
219
+ __name(computeSnapshot, "computeSnapshot");
220
+ var SERVER_SNAPSHOT = { status: "anonymous", accessExpiresAt: null };
221
+ var SESSION_SYNC_EVENT = `cfg-auth:changed:${ACCESS_KEY}`;
222
+ var _snapshot = computeSnapshot();
223
+ var _sessionListeners = /* @__PURE__ */ new Set();
224
+ var _expiryTimer = null;
225
+ var _storageListenerInstalled = false;
226
+ function scheduleExpiryFlip() {
227
+ if (!isBrowser) return;
228
+ if (_expiryTimer !== null) {
229
+ clearTimeout(_expiryTimer);
230
+ _expiryTimer = null;
231
+ }
232
+ if (_sessionListeners.size === 0) return;
233
+ const now = Date.now();
234
+ const exps = [];
235
+ const access = _storage.get(ACCESS_KEY);
236
+ const refresh = _storage.get(REFRESH_KEY);
237
+ const accessExp = access ? jwtExpMs(access) : null;
238
+ const refreshExp = refresh ? jwtExpMs(refresh) : null;
239
+ if (accessExp !== null && accessExp > now) exps.push(accessExp);
240
+ if (refreshExp !== null && refreshExp > now) exps.push(refreshExp);
241
+ if (!exps.length) return;
242
+ const delay = Math.min(Math.min(...exps) - now + 250, 2147483647);
243
+ _expiryTimer = setTimeout(notifySessionChanged, delay);
244
+ }
245
+ __name(scheduleExpiryFlip, "scheduleExpiryFlip");
246
+ function notifySessionChanged() {
247
+ const next = computeSnapshot();
248
+ const changed = next.status !== _snapshot.status || next.accessExpiresAt !== _snapshot.accessExpiresAt;
249
+ if (changed) _snapshot = next;
250
+ scheduleExpiryFlip();
251
+ if (!changed) return;
252
+ for (const listener of Array.from(_sessionListeners)) {
253
+ try {
254
+ listener();
255
+ } catch {
256
+ }
257
+ }
258
+ if (isBrowser) {
259
+ try {
260
+ window.dispatchEvent(new Event(SESSION_SYNC_EVENT));
261
+ } catch {
262
+ }
263
+ }
264
+ }
265
+ __name(notifySessionChanged, "notifySessionChanged");
266
+ function ensureStorageSync() {
267
+ if (!isBrowser || _storageListenerInstalled) return;
268
+ _storageListenerInstalled = true;
269
+ window.addEventListener("storage", (e) => {
270
+ if (e.key === null || e.key === ACCESS_KEY || e.key === REFRESH_KEY) {
271
+ notifySessionChanged();
272
+ }
273
+ });
274
+ window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
275
+ }
276
+ __name(ensureStorageSync, "ensureStorageSync");
277
+ var _sessionExpiredHandlers = /* @__PURE__ */ new Set();
278
+ var _client = null;
279
+ function pushClientConfig() {
280
+ if (!_client) return;
281
+ _client.setConfig({
282
+ baseUrl: auth.getBaseUrl(),
283
+ credentials: _withCredentials ? "include" : "same-origin"
284
+ });
285
+ }
286
+ __name(pushClientConfig, "pushClientConfig");
287
+ var auth = {
288
+ // ── Storage mode ──────────────────────────────────────────────────
289
+ getStorageMode() {
290
+ return _storageMode;
291
+ },
292
+ setStorageMode(mode) {
293
+ _storageMode = mode;
294
+ _storage = mode === "cookie" ? cookieBackend : localStorageBackend;
295
+ notifySessionChanged();
296
+ },
297
+ // ── Bearer token ──────────────────────────────────────────────────
298
+ getToken() {
299
+ return _storage.get(ACCESS_KEY);
300
+ },
301
+ setToken(token) {
302
+ _storage.set(ACCESS_KEY, token);
303
+ notifySessionChanged();
304
+ },
305
+ getRefreshToken() {
306
+ return _storage.get(REFRESH_KEY);
307
+ },
308
+ setRefreshToken(token) {
309
+ _storage.set(REFRESH_KEY, token);
310
+ notifySessionChanged();
311
+ },
312
+ clearTokens() {
313
+ _storage.set(ACCESS_KEY, null);
314
+ _storage.set(REFRESH_KEY, null);
315
+ notifySessionChanged();
316
+ },
317
+ /** Session-aware: token PRESENT and not past its `exp` (or a live refresh
318
+ * token exists that can mint one). Prefer `getSnapshot().status` in React. */
319
+ isAuthenticated() {
320
+ return computeSnapshot().status === "authenticated";
321
+ },
322
+ // ── Session (the ONE write path for login/logout flows) ──────────
323
+ /**
324
+ * Persist a token pair atomically. Every login flow (OTP verify, 2FA,
325
+ * OAuth callback) and the refresh handler should end here — do NOT
326
+ * scatter `setToken`/`setRefreshToken` pairs through app code.
327
+ * `refresh: undefined` keeps the current refresh token (access-only
328
+ * rotation); `refresh: null` explicitly drops it.
329
+ */
330
+ setSession(tokens) {
331
+ _storage.set(ACCESS_KEY, tokens.access);
332
+ if (tokens.refresh !== void 0) _storage.set(REFRESH_KEY, tokens.refresh);
333
+ notifySessionChanged();
334
+ },
335
+ clearSession() {
336
+ auth.clearTokens();
337
+ },
338
+ // ── Reactive snapshot (for useSyncExternalStore) ──────────────────
339
+ /**
340
+ * @example React:
341
+ * const session = useSyncExternalStore(
342
+ * auth.subscribe, auth.getSnapshot, auth.getServerSnapshot,
343
+ * );
344
+ * const isAuthenticated = session.status === 'authenticated';
345
+ */
346
+ getSnapshot() {
347
+ return _snapshot;
348
+ },
349
+ getServerSnapshot() {
350
+ return SERVER_SNAPSHOT;
351
+ },
352
+ subscribe(listener) {
353
+ ensureStorageSync();
354
+ _sessionListeners.add(listener);
355
+ notifySessionChanged();
356
+ return () => {
357
+ _sessionListeners.delete(listener);
358
+ if (_sessionListeners.size === 0 && _expiryTimer !== null) {
359
+ clearTimeout(_expiryTimer);
360
+ _expiryTimer = null;
361
+ }
362
+ };
363
+ },
364
+ /**
365
+ * Fired on TERMINAL 401 — after the refresh+retry path is exhausted.
366
+ * The store has already cleared the session before calling back, so
367
+ * handlers only need to route to login (no clear-then-redirect
368
+ * ordering to get wrong). Returns an unsubscribe function; multiple
369
+ * handlers compose (unlike the legacy single-slot `onUnauthorized`).
370
+ */
371
+ onSessionExpired(cb) {
372
+ _sessionExpiredHandlers.add(cb);
373
+ return () => {
374
+ _sessionExpiredHandlers.delete(cb);
375
+ };
376
+ },
377
+ // ── API key ───────────────────────────────────────────────────────
378
+ getApiKey() {
379
+ return _apiKeyOverride ?? _storage.get(API_KEY_KEY) ?? defaultApiKey();
380
+ },
381
+ setApiKey(key) {
382
+ _apiKeyOverride = key;
383
+ },
384
+ setApiKeyPersist(key) {
385
+ _apiKeyOverride = key;
386
+ _storage.set(API_KEY_KEY, key);
387
+ },
388
+ clearApiKey() {
389
+ _apiKeyOverride = null;
390
+ _storage.set(API_KEY_KEY, null);
391
+ },
392
+ // ── Locale ────────────────────────────────────────────────────────
393
+ getLocale() {
394
+ return _localeOverride ?? detectLocale();
395
+ },
396
+ setLocale(locale) {
397
+ _localeOverride = locale;
398
+ },
399
+ // ── Base URL ──────────────────────────────────────────────────────
400
+ getBaseUrl() {
401
+ const url = _baseUrlOverride ?? defaultBaseUrl();
402
+ return url.replace(/\/$/, "");
403
+ },
404
+ setBaseUrl(url) {
405
+ _baseUrlOverride = url ? url.replace(/\/$/, "") : null;
406
+ pushClientConfig();
407
+ },
408
+ // ── Credentials toggle ────────────────────────────────────────────
409
+ getWithCredentials() {
410
+ return _withCredentials;
411
+ },
412
+ setWithCredentials(value) {
413
+ _withCredentials = value;
414
+ pushClientConfig();
415
+ },
416
+ // ── 401 handler ───────────────────────────────────────────────────
417
+ /**
418
+ * Fired when the server returns 401 AND no refresh path recovers it
419
+ * (no refresh token, no refresh handler, refresh failed, or retry
420
+ * still 401). The app should clear local state and redirect to login.
421
+ *
422
+ * NOT fired for 401 that gets transparently recovered by the refresh
423
+ * handler — those are invisible to callers.
424
+ */
425
+ onUnauthorized(cb) {
426
+ _onUnauthorized = cb;
427
+ },
428
+ /**
429
+ * Register the refresh strategy. The handler receives the current
430
+ * refresh token and must call your refresh endpoint, returning
431
+ * `{ access, refresh? }` on success or `null` on failure.
432
+ *
433
+ * @example
434
+ * auth.setRefreshHandler(async (refresh) => {
435
+ * const { data } = await Auth.tokenRefreshCreate({ body: { refresh } });
436
+ * return data ? { access: data.access, refresh: data.refresh } : null;
437
+ * });
438
+ */
439
+ setRefreshHandler(fn) {
440
+ _refreshHandler = fn;
441
+ },
442
+ /**
443
+ * Proactively run the registered refresh handler right now, reusing the
444
+ * SAME single-flight promise as the 401-recovery interceptor. Callers
445
+ * (e.g. an expiry timer / focus / reconnect) get token rotation,
446
+ * de-duplication and rotated-token persistence for free — they must NOT
447
+ * re-implement any of it. Returns the fresh access token, or null if
448
+ * there is no handler / no refresh token / the refresh failed.
449
+ */
450
+ refreshNow() {
451
+ return tryRefresh();
452
+ }
453
+ };
454
+ async function tryRefresh() {
455
+ if (_refreshInflight) return _refreshInflight;
456
+ if (!_refreshHandler) return null;
457
+ const runRefresh = /* @__PURE__ */ __name(async () => {
458
+ const refresh = auth.getRefreshToken();
459
+ if (!refresh) return null;
460
+ const result = await _refreshHandler(refresh);
461
+ if (!result?.access) return null;
462
+ auth.setToken(result.access);
463
+ if (result.refresh) auth.setRefreshToken(result.refresh);
464
+ return result.access;
465
+ }, "runRefresh");
466
+ _refreshInflight = (async () => {
467
+ try {
468
+ const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
469
+ if (locks?.request) {
470
+ return await locks.request(`${REFRESH_KEY}:refresh`, runRefresh);
471
+ }
472
+ return await runRefresh();
473
+ } catch {
474
+ return null;
475
+ } finally {
476
+ _refreshInflight = null;
477
+ }
478
+ })();
479
+ return _refreshInflight;
480
+ }
481
+ __name(tryRefresh, "tryRefresh");
482
+ function expireSession(response) {
483
+ auth.clearTokens();
484
+ for (const cb of Array.from(_sessionExpiredHandlers)) {
485
+ try {
486
+ cb(response);
487
+ } catch {
488
+ }
489
+ }
490
+ if (_onUnauthorized) {
491
+ try {
492
+ _onUnauthorized(response);
493
+ } catch {
494
+ }
495
+ }
496
+ }
497
+ __name(expireSession, "expireSession");
498
+ function dpopEnabled() {
499
+ try {
500
+ return typeof process !== "undefined" && process.env?.NEXT_PUBLIC_DPOP_ENABLED === "true";
501
+ } catch {
502
+ return false;
503
+ }
504
+ }
505
+ __name(dpopEnabled, "dpopEnabled");
506
+ var _DPOP_DB = "cfg-auth";
507
+ var _DPOP_STORE = "keys";
508
+ var _DPOP_KEY_ID = "dpop-ec-p256";
509
+ function _idbOpen() {
510
+ return new Promise((resolve, reject) => {
511
+ const req = indexedDB.open(_DPOP_DB, 1);
512
+ req.onupgradeneeded = () => req.result.createObjectStore(_DPOP_STORE);
513
+ req.onsuccess = () => resolve(req.result);
514
+ req.onerror = () => reject(req.error);
515
+ });
516
+ }
517
+ __name(_idbOpen, "_idbOpen");
518
+ function _idbGet(key) {
519
+ return _idbOpen().then((db) => new Promise((resolve, reject) => {
520
+ const tx = db.transaction(_DPOP_STORE, "readonly");
521
+ const req = tx.objectStore(_DPOP_STORE).get(key);
522
+ req.onsuccess = () => resolve(req.result);
523
+ req.onerror = () => reject(req.error);
524
+ }));
525
+ }
526
+ __name(_idbGet, "_idbGet");
527
+ function _idbPut(key, value) {
528
+ return _idbOpen().then((db) => new Promise((resolve, reject) => {
529
+ const tx = db.transaction(_DPOP_STORE, "readwrite");
530
+ tx.objectStore(_DPOP_STORE).put(value, key);
531
+ tx.oncomplete = () => resolve();
532
+ tx.onerror = () => reject(tx.error);
533
+ }));
534
+ }
535
+ __name(_idbPut, "_idbPut");
536
+ var _dpopKeyPromise = null;
537
+ function _getDpopKeyPair() {
538
+ if (_dpopKeyPromise) return _dpopKeyPromise;
539
+ _dpopKeyPromise = (async () => {
540
+ const existing = await _idbGet(_DPOP_KEY_ID).catch(() => void 0);
541
+ if (existing) return existing;
542
+ const pair = await crypto.subtle.generateKey(
543
+ { name: "ECDSA", namedCurve: "P-256" },
544
+ false,
545
+ // extractable:false — JS can sign but never export the private key
546
+ ["sign"]
547
+ );
548
+ await _idbPut(_DPOP_KEY_ID, pair).catch(() => {
549
+ });
550
+ return pair;
551
+ })();
552
+ return _dpopKeyPromise;
553
+ }
554
+ __name(_getDpopKeyPair, "_getDpopKeyPair");
555
+ function _b64urlFromBytes(bytes) {
556
+ const arr = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);
557
+ let s = "";
558
+ for (let i = 0; i < arr.length; i++) s += String.fromCharCode(arr[i]);
559
+ return btoa(s).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
560
+ }
561
+ __name(_b64urlFromBytes, "_b64urlFromBytes");
562
+ function _b64urlFromString(str) {
563
+ return _b64urlFromBytes(new TextEncoder().encode(str));
564
+ }
565
+ __name(_b64urlFromString, "_b64urlFromString");
566
+ async function _publicJwk(pub) {
567
+ const jwk = await crypto.subtle.exportKey("jwk", pub);
568
+ return { kty: "EC", crv: "P-256", x: jwk.x, y: jwk.y };
569
+ }
570
+ __name(_publicJwk, "_publicJwk");
571
+ async function _makeDpopProof(method, url) {
572
+ try {
573
+ const pair = await _getDpopKeyPair();
574
+ const jwk = await _publicJwk(pair.publicKey);
575
+ const header = { typ: "dpop+jwt", alg: "ES256", jwk };
576
+ const htu = url.split("#")[0].split("?")[0];
577
+ const jti = crypto.randomUUID && crypto.randomUUID() || _b64urlFromBytes(crypto.getRandomValues(new Uint8Array(16)));
578
+ const payload = { htm: method.toUpperCase(), htu, iat: Math.floor(Date.now() / 1e3), jti };
579
+ const signingInput = `${_b64urlFromString(JSON.stringify(header))}.${_b64urlFromString(JSON.stringify(payload))}`;
580
+ const sig = await crypto.subtle.sign(
581
+ { name: "ECDSA", hash: "SHA-256" },
582
+ pair.privateKey,
583
+ new TextEncoder().encode(signingInput)
584
+ );
585
+ return `${signingInput}.${_b64urlFromBytes(sig)}`;
586
+ } catch {
587
+ return null;
588
+ }
589
+ }
590
+ __name(_makeDpopProof, "_makeDpopProof");
591
+ function installAuthOnClient(client2) {
592
+ if (_client) return;
593
+ _client = client2;
594
+ client2.setConfig({
595
+ baseUrl: auth.getBaseUrl(),
596
+ credentials: _withCredentials ? "include" : "same-origin"
597
+ });
598
+ client2.interceptors.request.use(async (request) => {
599
+ const token = auth.getToken();
600
+ if (token) request.headers.set("Authorization", `Bearer ${token}`);
601
+ const locale = auth.getLocale();
602
+ if (locale) request.headers.set("Accept-Language", locale);
603
+ const apiKey = auth.getApiKey();
604
+ if (apiKey && !request.headers.has("X-API-Key")) request.headers.set("X-API-Key", apiKey);
605
+ try {
606
+ const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
607
+ if (tz) request.headers.set("X-Timezone", tz);
608
+ } catch {
609
+ }
610
+ request.headers.set("X-Client-Time", (/* @__PURE__ */ new Date()).toISOString());
611
+ if (dpopEnabled() && typeof window !== "undefined") {
612
+ const proof = await _makeDpopProof(request.method, request.url);
613
+ if (proof) request.headers.set("DPoP", proof);
614
+ }
615
+ return request;
616
+ });
617
+ client2.interceptors.error.use((err, res, req) => {
618
+ if (err instanceof APIError) return err;
619
+ const url = req?.url ?? "";
620
+ const status = res?.status ?? 0;
621
+ const statusText = res?.statusText ?? "";
622
+ return new APIError(status, statusText, err, url);
623
+ });
624
+ client2.interceptors.response.use(async (response, request) => {
625
+ if (response.status !== 401) return response;
626
+ if (request.headers.get(RETRY_MARKER)) {
627
+ expireSession(response);
628
+ return response;
629
+ }
630
+ const newToken = await tryRefresh();
631
+ if (!newToken) {
632
+ expireSession(response);
633
+ return response;
634
+ }
635
+ const retry = request.clone();
636
+ retry.headers.set("Authorization", `Bearer ${newToken}`);
637
+ retry.headers.set(RETRY_MARKER, "1");
638
+ if (dpopEnabled() && typeof window !== "undefined") {
639
+ const proof = await _makeDpopProof(retry.method, retry.url);
640
+ if (proof) retry.headers.set("DPoP", proof);
641
+ }
642
+ try {
643
+ const retried = await fetch(retry);
644
+ if (retried.status === 401) expireSession(retried);
645
+ return retried;
646
+ } catch {
647
+ return response;
648
+ }
649
+ });
650
+ }
651
+ __name(installAuthOnClient, "installAuthOnClient");
652
+ var REFRESH_ENDPOINT = "/cfg/accounts/token/refresh/";
653
+ auth.setRefreshHandler(async (refresh) => {
654
+ try {
655
+ const url = `${auth.getBaseUrl()}${REFRESH_ENDPOINT}`;
656
+ const headers = { "Content-Type": "application/json" };
657
+ if (dpopEnabled() && typeof window !== "undefined") {
658
+ const proof = await _makeDpopProof("POST", url);
659
+ if (proof) headers["DPoP"] = proof;
660
+ }
661
+ const res = await fetch(url, {
662
+ method: "POST",
663
+ headers,
664
+ credentials: auth.getWithCredentials() ? "include" : "same-origin",
665
+ body: JSON.stringify({ refresh })
666
+ });
667
+ if (!res.ok) return null;
668
+ const data = await res.json();
669
+ return data?.access ? { access: data.access, refresh: data.refresh ?? refresh } : null;
670
+ } catch {
671
+ return null;
672
+ }
673
+ });
674
+
40
675
  // src/_api/generated/helpers/logger.ts
41
676
  import { createConsola } from "consola";
42
677
  var DEFAULT_CONFIG = {
@@ -133,9 +768,9 @@ var defaultLogger = new APILogger();
133
768
 
134
769
  // src/auth/utils/env.ts
135
770
  var isDev = process.env.NODE_ENV === "development";
136
- var isBrowser = typeof window !== "undefined";
771
+ var isBrowser2 = typeof window !== "undefined";
137
772
  var isStaticBuild = process.env.NEXT_PUBLIC_STATIC_BUILD === "true";
138
- var dpopEnabled = process.env.NEXT_PUBLIC_DPOP_ENABLED === "true";
773
+ var dpopEnabled2 = process.env.NEXT_PUBLIC_DPOP_ENABLED === "true";
139
774
 
140
775
  // src/log-control.ts
141
776
  var verboseByDefault = isDev || isStaticBuild;
@@ -447,53 +1082,1542 @@ var useAutoAuth = /* @__PURE__ */ __name((options = {}) => {
447
1082
  }, "useAutoAuth");
448
1083
 
449
1084
  // src/auth/hooks/useTwoFactor.ts
450
- import { useCallback as useCallback4, useState as useState3 } from "react";
1085
+ import { useCallback as useCallback4, useState as useState4 } from "react";
451
1086
 
452
- // src/auth/utils/analytics.ts
453
- var AnalyticsEvent = /* @__PURE__ */ ((AnalyticsEvent2) => {
454
- AnalyticsEvent2["AUTH_OTP_REQUEST"] = "auth_otp_request";
455
- AnalyticsEvent2["AUTH_LOGIN_SUCCESS"] = "auth_login_success";
456
- AnalyticsEvent2["AUTH_OTP_VERIFY_FAIL"] = "auth_otp_verify_fail";
457
- AnalyticsEvent2["AUTH_SESSION_EXPIRED"] = "auth_session_expired";
458
- AnalyticsEvent2["AUTH_TOKEN_REFRESH"] = "auth_token_refresh";
459
- AnalyticsEvent2["AUTH_TOKEN_REFRESH_FAIL"] = "auth_token_refresh_fail";
460
- AnalyticsEvent2["AUTH_LOGOUT"] = "auth_logout";
461
- AnalyticsEvent2["AUTH_OAUTH_START"] = "auth_oauth_start";
462
- AnalyticsEvent2["AUTH_OAUTH_FAIL"] = "auth_oauth_fail";
463
- return AnalyticsEvent2;
464
- })(AnalyticsEvent || {});
465
- var AnalyticsCategory = /* @__PURE__ */ ((AnalyticsCategory2) => {
466
- AnalyticsCategory2["AUTH"] = "auth";
467
- return AnalyticsCategory2;
468
- })(AnalyticsCategory || {});
469
- var Analytics = {
470
- /**
471
- * Track an analytics event
472
- */
473
- event(eventName, params) {
474
- if (isDev) {
475
- console.log("[Analytics]", eventName, params);
1087
+ // src/_api/generated/core/bodySerializer.gen.ts
1088
+ var serializeFormDataPair = /* @__PURE__ */ __name((data, key, value) => {
1089
+ if (typeof value === "string" || value instanceof Blob) {
1090
+ data.append(key, value);
1091
+ } else if (value instanceof Date) {
1092
+ data.append(key, value.toISOString());
1093
+ } else {
1094
+ data.append(key, JSON.stringify(value));
1095
+ }
1096
+ }, "serializeFormDataPair");
1097
+ var formDataBodySerializer = {
1098
+ bodySerializer: /* @__PURE__ */ __name((body) => {
1099
+ const data = new FormData();
1100
+ Object.entries(body).forEach(([key, value]) => {
1101
+ if (value === void 0 || value === null) {
1102
+ return;
1103
+ }
1104
+ if (Array.isArray(value)) {
1105
+ value.forEach((v) => serializeFormDataPair(data, key, v));
1106
+ } else {
1107
+ serializeFormDataPair(data, key, value);
1108
+ }
1109
+ });
1110
+ return data;
1111
+ }, "bodySerializer")
1112
+ };
1113
+ var jsonBodySerializer = {
1114
+ bodySerializer: /* @__PURE__ */ __name((body) => JSON.stringify(body, (_key, value) => typeof value === "bigint" ? value.toString() : value), "bodySerializer")
1115
+ };
1116
+
1117
+ // src/_api/generated/core/params.gen.ts
1118
+ var extraPrefixesMap = {
1119
+ $body_: "body",
1120
+ $headers_: "headers",
1121
+ $path_: "path",
1122
+ $query_: "query"
1123
+ };
1124
+ var extraPrefixes = Object.entries(extraPrefixesMap);
1125
+
1126
+ // src/_api/generated/core/serverSentEvents.gen.ts
1127
+ function createSseClient({
1128
+ onRequest,
1129
+ onSseError,
1130
+ onSseEvent,
1131
+ responseTransformer,
1132
+ responseValidator,
1133
+ sseDefaultRetryDelay,
1134
+ sseMaxRetryAttempts,
1135
+ sseMaxRetryDelay,
1136
+ sseSleepFn,
1137
+ url,
1138
+ ...options
1139
+ }) {
1140
+ let lastEventId;
1141
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
1142
+ const createStream = /* @__PURE__ */ __name(async function* () {
1143
+ let retryDelay = sseDefaultRetryDelay ?? 3e3;
1144
+ let attempt = 0;
1145
+ const signal = options.signal ?? new AbortController().signal;
1146
+ while (true) {
1147
+ if (signal.aborted) break;
1148
+ attempt++;
1149
+ const headers = options.headers instanceof Headers ? options.headers : new Headers(options.headers);
1150
+ if (lastEventId !== void 0) {
1151
+ headers.set("Last-Event-ID", lastEventId);
1152
+ }
1153
+ try {
1154
+ const requestInit = {
1155
+ redirect: "follow",
1156
+ ...options,
1157
+ body: options.serializedBody,
1158
+ headers,
1159
+ signal
1160
+ };
1161
+ let request = new Request(url, requestInit);
1162
+ if (onRequest) {
1163
+ request = await onRequest(url, requestInit);
1164
+ }
1165
+ const _fetch = options.fetch ?? globalThis.fetch;
1166
+ const response = await _fetch(request);
1167
+ if (!response.ok) throw new Error(`SSE failed: ${response.status} ${response.statusText}`);
1168
+ if (!response.body) throw new Error("No body in SSE response");
1169
+ const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
1170
+ let buffer = "";
1171
+ const abortHandler = /* @__PURE__ */ __name(() => {
1172
+ try {
1173
+ reader.cancel();
1174
+ } catch {
1175
+ }
1176
+ }, "abortHandler");
1177
+ signal.addEventListener("abort", abortHandler);
1178
+ try {
1179
+ while (true) {
1180
+ const { done, value } = await reader.read();
1181
+ if (done) break;
1182
+ buffer += value;
1183
+ buffer = buffer.replace(/\r\n?/g, "\n");
1184
+ const chunks = buffer.split("\n\n");
1185
+ buffer = chunks.pop() ?? "";
1186
+ for (const chunk of chunks) {
1187
+ const lines = chunk.split("\n");
1188
+ const dataLines = [];
1189
+ let eventName;
1190
+ for (const line of lines) {
1191
+ if (line.startsWith("data:")) {
1192
+ dataLines.push(line.replace(/^data:\s*/, ""));
1193
+ } else if (line.startsWith("event:")) {
1194
+ eventName = line.replace(/^event:\s*/, "");
1195
+ } else if (line.startsWith("id:")) {
1196
+ lastEventId = line.replace(/^id:\s*/, "");
1197
+ } else if (line.startsWith("retry:")) {
1198
+ const parsed = Number.parseInt(line.replace(/^retry:\s*/, ""), 10);
1199
+ if (!Number.isNaN(parsed)) {
1200
+ retryDelay = parsed;
1201
+ }
1202
+ }
1203
+ }
1204
+ let data;
1205
+ let parsedJson = false;
1206
+ if (dataLines.length) {
1207
+ const rawData = dataLines.join("\n");
1208
+ try {
1209
+ data = JSON.parse(rawData);
1210
+ parsedJson = true;
1211
+ } catch {
1212
+ data = rawData;
1213
+ }
1214
+ }
1215
+ if (parsedJson) {
1216
+ if (responseValidator) {
1217
+ await responseValidator(data);
1218
+ }
1219
+ if (responseTransformer) {
1220
+ data = await responseTransformer(data);
1221
+ }
1222
+ }
1223
+ onSseEvent?.({
1224
+ data,
1225
+ event: eventName,
1226
+ id: lastEventId,
1227
+ retry: retryDelay
1228
+ });
1229
+ if (dataLines.length) {
1230
+ yield data;
1231
+ }
1232
+ }
1233
+ }
1234
+ } finally {
1235
+ signal.removeEventListener("abort", abortHandler);
1236
+ reader.releaseLock();
1237
+ }
1238
+ break;
1239
+ } catch (error) {
1240
+ onSseError?.(error);
1241
+ if (sseMaxRetryAttempts !== void 0 && attempt >= sseMaxRetryAttempts) {
1242
+ break;
1243
+ }
1244
+ const backoff = Math.min(retryDelay * 2 ** (attempt - 1), sseMaxRetryDelay ?? 3e4);
1245
+ await sleep(backoff);
1246
+ }
476
1247
  }
477
- },
478
- /**
479
- * Set user ID for tracking
480
- */
481
- setUser(userId) {
1248
+ }, "createStream");
1249
+ const stream = createStream();
1250
+ return { stream };
1251
+ }
1252
+ __name(createSseClient, "createSseClient");
1253
+
1254
+ // src/_api/generated/core/pathSerializer.gen.ts
1255
+ var separatorArrayExplode = /* @__PURE__ */ __name((style) => {
1256
+ switch (style) {
1257
+ case "label":
1258
+ return ".";
1259
+ case "matrix":
1260
+ return ";";
1261
+ case "simple":
1262
+ return ",";
1263
+ default:
1264
+ return "&";
1265
+ }
1266
+ }, "separatorArrayExplode");
1267
+ var separatorArrayNoExplode = /* @__PURE__ */ __name((style) => {
1268
+ switch (style) {
1269
+ case "form":
1270
+ return ",";
1271
+ case "pipeDelimited":
1272
+ return "|";
1273
+ case "spaceDelimited":
1274
+ return "%20";
1275
+ default:
1276
+ return ",";
1277
+ }
1278
+ }, "separatorArrayNoExplode");
1279
+ var separatorObjectExplode = /* @__PURE__ */ __name((style) => {
1280
+ switch (style) {
1281
+ case "label":
1282
+ return ".";
1283
+ case "matrix":
1284
+ return ";";
1285
+ case "simple":
1286
+ return ",";
1287
+ default:
1288
+ return "&";
1289
+ }
1290
+ }, "separatorObjectExplode");
1291
+ var serializeArrayParam = /* @__PURE__ */ __name(({
1292
+ allowReserved,
1293
+ explode,
1294
+ name,
1295
+ style,
1296
+ value
1297
+ }) => {
1298
+ if (!explode) {
1299
+ const joinedValues2 = (allowReserved ? value : value.map((v) => encodeURIComponent(v))).join(separatorArrayNoExplode(style));
1300
+ switch (style) {
1301
+ case "label":
1302
+ return `.${joinedValues2}`;
1303
+ case "matrix":
1304
+ return `;${name}=${joinedValues2}`;
1305
+ case "simple":
1306
+ return joinedValues2;
1307
+ default:
1308
+ return `${name}=${joinedValues2}`;
1309
+ }
1310
+ }
1311
+ const separator = separatorArrayExplode(style);
1312
+ const joinedValues = value.map((v) => {
1313
+ if (style === "label" || style === "simple") {
1314
+ return allowReserved ? v : encodeURIComponent(v);
1315
+ }
1316
+ return serializePrimitiveParam({
1317
+ allowReserved,
1318
+ name,
1319
+ value: v
1320
+ });
1321
+ }).join(separator);
1322
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
1323
+ }, "serializeArrayParam");
1324
+ var serializePrimitiveParam = /* @__PURE__ */ __name(({
1325
+ allowReserved,
1326
+ name,
1327
+ value
1328
+ }) => {
1329
+ if (value === void 0 || value === null) {
1330
+ return "";
1331
+ }
1332
+ if (typeof value === "object") {
1333
+ throw new Error(
1334
+ "Deeply-nested arrays/objects aren\u2019t supported. Provide your own `querySerializer()` to handle these."
1335
+ );
1336
+ }
1337
+ return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
1338
+ }, "serializePrimitiveParam");
1339
+ var serializeObjectParam = /* @__PURE__ */ __name(({
1340
+ allowReserved,
1341
+ explode,
1342
+ name,
1343
+ style,
1344
+ value,
1345
+ valueOnly
1346
+ }) => {
1347
+ if (value instanceof Date) {
1348
+ return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
1349
+ }
1350
+ if (style !== "deepObject" && !explode) {
1351
+ let values = [];
1352
+ Object.entries(value).forEach(([key, v]) => {
1353
+ values = [...values, key, allowReserved ? v : encodeURIComponent(v)];
1354
+ });
1355
+ const joinedValues2 = values.join(",");
1356
+ switch (style) {
1357
+ case "form":
1358
+ return `${name}=${joinedValues2}`;
1359
+ case "label":
1360
+ return `.${joinedValues2}`;
1361
+ case "matrix":
1362
+ return `;${name}=${joinedValues2}`;
1363
+ default:
1364
+ return joinedValues2;
1365
+ }
1366
+ }
1367
+ const separator = separatorObjectExplode(style);
1368
+ const joinedValues = Object.entries(value).map(
1369
+ ([key, v]) => serializePrimitiveParam({
1370
+ allowReserved,
1371
+ name: style === "deepObject" ? `${name}[${key}]` : key,
1372
+ value: v
1373
+ })
1374
+ ).join(separator);
1375
+ return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
1376
+ }, "serializeObjectParam");
1377
+
1378
+ // src/_api/generated/core/utils.gen.ts
1379
+ var PATH_PARAM_RE = /\{[^{}]+\}/g;
1380
+ var defaultPathSerializer = /* @__PURE__ */ __name(({ path, url: _url }) => {
1381
+ let url = _url;
1382
+ const matches = _url.match(PATH_PARAM_RE);
1383
+ if (matches) {
1384
+ for (const match of matches) {
1385
+ let explode = false;
1386
+ let name = match.substring(1, match.length - 1);
1387
+ let style = "simple";
1388
+ if (name.endsWith("*")) {
1389
+ explode = true;
1390
+ name = name.substring(0, name.length - 1);
1391
+ }
1392
+ if (name.startsWith(".")) {
1393
+ name = name.substring(1);
1394
+ style = "label";
1395
+ } else if (name.startsWith(";")) {
1396
+ name = name.substring(1);
1397
+ style = "matrix";
1398
+ }
1399
+ const value = path[name];
1400
+ if (value === void 0 || value === null) {
1401
+ continue;
1402
+ }
1403
+ if (Array.isArray(value)) {
1404
+ url = url.replace(match, serializeArrayParam({ explode, name, style, value }));
1405
+ continue;
1406
+ }
1407
+ if (typeof value === "object") {
1408
+ url = url.replace(
1409
+ match,
1410
+ serializeObjectParam({
1411
+ explode,
1412
+ name,
1413
+ style,
1414
+ value,
1415
+ valueOnly: true
1416
+ })
1417
+ );
1418
+ continue;
1419
+ }
1420
+ if (style === "matrix") {
1421
+ url = url.replace(
1422
+ match,
1423
+ `;${serializePrimitiveParam({
1424
+ name,
1425
+ value
1426
+ })}`
1427
+ );
1428
+ continue;
1429
+ }
1430
+ const replaceValue = encodeURIComponent(
1431
+ style === "label" ? `.${value}` : value
1432
+ );
1433
+ url = url.replace(match, replaceValue);
1434
+ }
1435
+ }
1436
+ return url;
1437
+ }, "defaultPathSerializer");
1438
+ var getUrl = /* @__PURE__ */ __name(({
1439
+ baseUrl,
1440
+ path,
1441
+ query,
1442
+ querySerializer,
1443
+ url: _url
1444
+ }) => {
1445
+ const pathUrl = _url.startsWith("/") ? _url : `/${_url}`;
1446
+ let url = (baseUrl ?? "") + pathUrl;
1447
+ if (path) {
1448
+ url = defaultPathSerializer({ path, url });
1449
+ }
1450
+ let search = query ? querySerializer(query) : "";
1451
+ if (search.startsWith("?")) {
1452
+ search = search.substring(1);
1453
+ }
1454
+ if (search) {
1455
+ url += `?${search}`;
1456
+ }
1457
+ return url;
1458
+ }, "getUrl");
1459
+ function getValidRequestBody(options) {
1460
+ const hasBody = options.body !== void 0;
1461
+ const isSerializedBody = hasBody && options.bodySerializer;
1462
+ if (isSerializedBody) {
1463
+ if ("serializedBody" in options) {
1464
+ const hasSerializedBody = options.serializedBody !== void 0 && options.serializedBody !== "";
1465
+ return hasSerializedBody ? options.serializedBody : null;
1466
+ }
1467
+ return options.body !== "" ? options.body : null;
1468
+ }
1469
+ if (hasBody) {
1470
+ return options.body;
1471
+ }
1472
+ return void 0;
1473
+ }
1474
+ __name(getValidRequestBody, "getValidRequestBody");
1475
+
1476
+ // src/_api/generated/core/auth.gen.ts
1477
+ var getAuthToken = /* @__PURE__ */ __name(async (auth2, callback) => {
1478
+ const token = typeof callback === "function" ? await callback(auth2) : callback;
1479
+ if (!token) {
1480
+ return;
1481
+ }
1482
+ if (auth2.scheme === "bearer") {
1483
+ return `Bearer ${token}`;
1484
+ }
1485
+ if (auth2.scheme === "basic") {
1486
+ return `Basic ${btoa(token)}`;
1487
+ }
1488
+ return token;
1489
+ }, "getAuthToken");
1490
+
1491
+ // src/_api/generated/client/utils.gen.ts
1492
+ var createQuerySerializer = /* @__PURE__ */ __name(({
1493
+ parameters = {},
1494
+ ...args
1495
+ } = {}) => {
1496
+ const querySerializer = /* @__PURE__ */ __name((queryParams) => {
1497
+ const search = [];
1498
+ if (queryParams && typeof queryParams === "object") {
1499
+ for (const name in queryParams) {
1500
+ const value = queryParams[name];
1501
+ if (value === void 0 || value === null) {
1502
+ continue;
1503
+ }
1504
+ const options = parameters[name] || args;
1505
+ if (Array.isArray(value)) {
1506
+ const serializedArray = serializeArrayParam({
1507
+ allowReserved: options.allowReserved,
1508
+ explode: true,
1509
+ name,
1510
+ style: "form",
1511
+ value,
1512
+ ...options.array
1513
+ });
1514
+ if (serializedArray) search.push(serializedArray);
1515
+ } else if (typeof value === "object") {
1516
+ const serializedObject = serializeObjectParam({
1517
+ allowReserved: options.allowReserved,
1518
+ explode: true,
1519
+ name,
1520
+ style: "deepObject",
1521
+ value,
1522
+ ...options.object
1523
+ });
1524
+ if (serializedObject) search.push(serializedObject);
1525
+ } else {
1526
+ const serializedPrimitive = serializePrimitiveParam({
1527
+ allowReserved: options.allowReserved,
1528
+ name,
1529
+ value
1530
+ });
1531
+ if (serializedPrimitive) search.push(serializedPrimitive);
1532
+ }
1533
+ }
1534
+ }
1535
+ return search.join("&");
1536
+ }, "querySerializer");
1537
+ return querySerializer;
1538
+ }, "createQuerySerializer");
1539
+ var getParseAs = /* @__PURE__ */ __name((contentType) => {
1540
+ if (!contentType) {
1541
+ return "stream";
1542
+ }
1543
+ const cleanContent = contentType.split(";")[0]?.trim();
1544
+ if (!cleanContent) {
1545
+ return;
1546
+ }
1547
+ if (cleanContent.startsWith("application/json") || cleanContent.endsWith("+json")) {
1548
+ return "json";
1549
+ }
1550
+ if (cleanContent === "multipart/form-data") {
1551
+ return "formData";
1552
+ }
1553
+ if (["application/", "audio/", "image/", "video/"].some((type) => cleanContent.startsWith(type))) {
1554
+ return "blob";
1555
+ }
1556
+ if (cleanContent.startsWith("text/")) {
1557
+ return "text";
1558
+ }
1559
+ return;
1560
+ }, "getParseAs");
1561
+ var checkForExistence = /* @__PURE__ */ __name((options, name) => {
1562
+ if (!name) {
1563
+ return false;
1564
+ }
1565
+ if (options.headers.has(name) || options.query?.[name] || options.headers.get("Cookie")?.includes(`${name}=`)) {
1566
+ return true;
1567
+ }
1568
+ return false;
1569
+ }, "checkForExistence");
1570
+ async function setAuthParams(options) {
1571
+ for (const auth2 of options.security ?? []) {
1572
+ if (checkForExistence(options, auth2.name)) {
1573
+ continue;
1574
+ }
1575
+ const token = await getAuthToken(auth2, options.auth);
1576
+ if (!token) {
1577
+ continue;
1578
+ }
1579
+ const name = auth2.name ?? "Authorization";
1580
+ switch (auth2.in) {
1581
+ case "query":
1582
+ if (!options.query) {
1583
+ options.query = {};
1584
+ }
1585
+ options.query[name] = token;
1586
+ break;
1587
+ case "cookie":
1588
+ options.headers.append("Cookie", `${name}=${token}`);
1589
+ break;
1590
+ case "header":
1591
+ default:
1592
+ options.headers.set(name, token);
1593
+ break;
1594
+ }
1595
+ }
1596
+ }
1597
+ __name(setAuthParams, "setAuthParams");
1598
+ var buildUrl = /* @__PURE__ */ __name((options) => getUrl({
1599
+ baseUrl: options.baseUrl,
1600
+ path: options.path,
1601
+ query: options.query,
1602
+ querySerializer: typeof options.querySerializer === "function" ? options.querySerializer : createQuerySerializer(options.querySerializer),
1603
+ url: options.url
1604
+ }), "buildUrl");
1605
+ var mergeConfigs = /* @__PURE__ */ __name((a, b) => {
1606
+ const config = { ...a, ...b };
1607
+ if (config.baseUrl?.endsWith("/")) {
1608
+ config.baseUrl = config.baseUrl.substring(0, config.baseUrl.length - 1);
1609
+ }
1610
+ config.headers = mergeHeaders(a.headers, b.headers);
1611
+ return config;
1612
+ }, "mergeConfigs");
1613
+ var headersEntries = /* @__PURE__ */ __name((headers) => {
1614
+ const entries = [];
1615
+ headers.forEach((value, key) => {
1616
+ entries.push([key, value]);
1617
+ });
1618
+ return entries;
1619
+ }, "headersEntries");
1620
+ var mergeHeaders = /* @__PURE__ */ __name((...headers) => {
1621
+ const mergedHeaders = new Headers();
1622
+ for (const header of headers) {
1623
+ if (!header) {
1624
+ continue;
1625
+ }
1626
+ const iterator = header instanceof Headers ? headersEntries(header) : Object.entries(header);
1627
+ for (const [key, value] of iterator) {
1628
+ if (value === null) {
1629
+ mergedHeaders.delete(key);
1630
+ } else if (Array.isArray(value)) {
1631
+ for (const v of value) {
1632
+ mergedHeaders.append(key, v);
1633
+ }
1634
+ } else if (value !== void 0) {
1635
+ mergedHeaders.set(
1636
+ key,
1637
+ typeof value === "object" ? JSON.stringify(value) : value
1638
+ );
1639
+ }
1640
+ }
1641
+ }
1642
+ return mergedHeaders;
1643
+ }, "mergeHeaders");
1644
+ var Interceptors = class {
1645
+ static {
1646
+ __name(this, "Interceptors");
1647
+ }
1648
+ fns = [];
1649
+ clear() {
1650
+ this.fns = [];
1651
+ }
1652
+ eject(id) {
1653
+ const index = this.getInterceptorIndex(id);
1654
+ if (this.fns[index]) {
1655
+ this.fns[index] = null;
1656
+ }
1657
+ }
1658
+ exists(id) {
1659
+ const index = this.getInterceptorIndex(id);
1660
+ return Boolean(this.fns[index]);
1661
+ }
1662
+ getInterceptorIndex(id) {
1663
+ if (typeof id === "number") {
1664
+ return this.fns[id] ? id : -1;
1665
+ }
1666
+ return this.fns.indexOf(id);
1667
+ }
1668
+ update(id, fn) {
1669
+ const index = this.getInterceptorIndex(id);
1670
+ if (this.fns[index]) {
1671
+ this.fns[index] = fn;
1672
+ return id;
1673
+ }
1674
+ return false;
1675
+ }
1676
+ use(fn) {
1677
+ this.fns.push(fn);
1678
+ return this.fns.length - 1;
1679
+ }
1680
+ };
1681
+ var createInterceptors = /* @__PURE__ */ __name(() => ({
1682
+ error: new Interceptors(),
1683
+ request: new Interceptors(),
1684
+ response: new Interceptors()
1685
+ }), "createInterceptors");
1686
+ var defaultQuerySerializer = createQuerySerializer({
1687
+ allowReserved: false,
1688
+ array: {
1689
+ explode: true,
1690
+ style: "form"
1691
+ },
1692
+ object: {
1693
+ explode: true,
1694
+ style: "deepObject"
1695
+ }
1696
+ });
1697
+ var defaultHeaders = {
1698
+ "Content-Type": "application/json"
1699
+ };
1700
+ var createConfig = /* @__PURE__ */ __name((override = {}) => ({
1701
+ ...jsonBodySerializer,
1702
+ headers: defaultHeaders,
1703
+ parseAs: "auto",
1704
+ querySerializer: defaultQuerySerializer,
1705
+ ...override
1706
+ }), "createConfig");
1707
+
1708
+ // src/_api/generated/client/client.gen.ts
1709
+ var createClient = /* @__PURE__ */ __name((config = {}) => {
1710
+ let _config = mergeConfigs(createConfig(), config);
1711
+ const getConfig = /* @__PURE__ */ __name(() => ({ ..._config }), "getConfig");
1712
+ const setConfig = /* @__PURE__ */ __name((config2) => {
1713
+ _config = mergeConfigs(_config, config2);
1714
+ return getConfig();
1715
+ }, "setConfig");
1716
+ const interceptors = createInterceptors();
1717
+ const beforeRequest = /* @__PURE__ */ __name(async (options) => {
1718
+ const opts = {
1719
+ ..._config,
1720
+ ...options,
1721
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
1722
+ headers: mergeHeaders(_config.headers, options.headers),
1723
+ serializedBody: void 0
1724
+ };
1725
+ if (opts.security) {
1726
+ await setAuthParams(opts);
1727
+ }
1728
+ if (opts.requestValidator) {
1729
+ await opts.requestValidator(opts);
1730
+ }
1731
+ if (opts.body !== void 0 && opts.bodySerializer) {
1732
+ opts.serializedBody = opts.bodySerializer(opts.body);
1733
+ }
1734
+ if (opts.body === void 0 || opts.serializedBody === "") {
1735
+ opts.headers.delete("Content-Type");
1736
+ }
1737
+ const resolvedOpts = opts;
1738
+ const url = buildUrl(resolvedOpts);
1739
+ return { opts: resolvedOpts, url };
1740
+ }, "beforeRequest");
1741
+ const request = /* @__PURE__ */ __name(async (options) => {
1742
+ const throwOnError = options.throwOnError ?? _config.throwOnError;
1743
+ const responseStyle = options.responseStyle ?? _config.responseStyle;
1744
+ let request2;
1745
+ let response;
1746
+ try {
1747
+ const { opts, url } = await beforeRequest(options);
1748
+ const requestInit = {
1749
+ redirect: "follow",
1750
+ ...opts,
1751
+ body: getValidRequestBody(opts)
1752
+ };
1753
+ request2 = new Request(url, requestInit);
1754
+ for (const fn of interceptors.request.fns) {
1755
+ if (fn) {
1756
+ request2 = await fn(request2, opts);
1757
+ }
1758
+ }
1759
+ const _fetch = opts.fetch;
1760
+ response = await _fetch(request2);
1761
+ for (const fn of interceptors.response.fns) {
1762
+ if (fn) {
1763
+ response = await fn(response, request2, opts);
1764
+ }
1765
+ }
1766
+ const result = {
1767
+ request: request2,
1768
+ response
1769
+ };
1770
+ if (response.ok) {
1771
+ const parseAs = (opts.parseAs === "auto" ? getParseAs(response.headers.get("Content-Type")) : opts.parseAs) ?? "json";
1772
+ if (response.status === 204 || response.headers.get("Content-Length") === "0") {
1773
+ let emptyData;
1774
+ switch (parseAs) {
1775
+ case "arrayBuffer":
1776
+ case "blob":
1777
+ case "text":
1778
+ emptyData = await response[parseAs]();
1779
+ break;
1780
+ case "formData":
1781
+ emptyData = new FormData();
1782
+ break;
1783
+ case "stream":
1784
+ emptyData = response.body;
1785
+ break;
1786
+ case "json":
1787
+ default:
1788
+ emptyData = {};
1789
+ break;
1790
+ }
1791
+ return opts.responseStyle === "data" ? emptyData : {
1792
+ data: emptyData,
1793
+ ...result
1794
+ };
1795
+ }
1796
+ let data;
1797
+ switch (parseAs) {
1798
+ case "arrayBuffer":
1799
+ case "blob":
1800
+ case "formData":
1801
+ case "text":
1802
+ data = await response[parseAs]();
1803
+ break;
1804
+ case "json": {
1805
+ const text = await response.text();
1806
+ data = text ? JSON.parse(text) : {};
1807
+ break;
1808
+ }
1809
+ case "stream":
1810
+ return opts.responseStyle === "data" ? response.body : {
1811
+ data: response.body,
1812
+ ...result
1813
+ };
1814
+ }
1815
+ if (parseAs === "json") {
1816
+ if (opts.responseValidator) {
1817
+ await opts.responseValidator(data);
1818
+ }
1819
+ if (opts.responseTransformer) {
1820
+ data = await opts.responseTransformer(data);
1821
+ }
1822
+ }
1823
+ return opts.responseStyle === "data" ? data : {
1824
+ data,
1825
+ ...result
1826
+ };
1827
+ }
1828
+ const textError = await response.text();
1829
+ let jsonError;
1830
+ try {
1831
+ jsonError = JSON.parse(textError);
1832
+ } catch {
1833
+ }
1834
+ throw jsonError ?? textError;
1835
+ } catch (error) {
1836
+ let finalError = error;
1837
+ for (const fn of interceptors.error.fns) {
1838
+ if (fn) {
1839
+ finalError = await fn(finalError, response, request2, options);
1840
+ }
1841
+ }
1842
+ finalError = finalError || {};
1843
+ if (throwOnError) {
1844
+ throw finalError;
1845
+ }
1846
+ return responseStyle === "data" ? void 0 : {
1847
+ error: finalError,
1848
+ request: request2,
1849
+ response
1850
+ };
1851
+ }
1852
+ }, "request");
1853
+ const makeMethodFn = /* @__PURE__ */ __name((method) => (options) => request({ ...options, method }), "makeMethodFn");
1854
+ const makeSseFn = /* @__PURE__ */ __name((method) => async (options) => {
1855
+ const { opts, url } = await beforeRequest(options);
1856
+ return createSseClient({
1857
+ ...opts,
1858
+ body: opts.body,
1859
+ method,
1860
+ onRequest: /* @__PURE__ */ __name(async (url2, init) => {
1861
+ let request2 = new Request(url2, init);
1862
+ for (const fn of interceptors.request.fns) {
1863
+ if (fn) {
1864
+ request2 = await fn(request2, opts);
1865
+ }
1866
+ }
1867
+ return request2;
1868
+ }, "onRequest"),
1869
+ serializedBody: getValidRequestBody(opts),
1870
+ url
1871
+ });
1872
+ }, "makeSseFn");
1873
+ const _buildUrl = /* @__PURE__ */ __name((options) => buildUrl({ ..._config, ...options }), "_buildUrl");
1874
+ return {
1875
+ buildUrl: _buildUrl,
1876
+ connect: makeMethodFn("CONNECT"),
1877
+ delete: makeMethodFn("DELETE"),
1878
+ get: makeMethodFn("GET"),
1879
+ getConfig,
1880
+ head: makeMethodFn("HEAD"),
1881
+ interceptors,
1882
+ options: makeMethodFn("OPTIONS"),
1883
+ patch: makeMethodFn("PATCH"),
1884
+ post: makeMethodFn("POST"),
1885
+ put: makeMethodFn("PUT"),
1886
+ request,
1887
+ setConfig,
1888
+ sse: {
1889
+ connect: makeSseFn("CONNECT"),
1890
+ delete: makeSseFn("DELETE"),
1891
+ get: makeSseFn("GET"),
1892
+ head: makeSseFn("HEAD"),
1893
+ options: makeSseFn("OPTIONS"),
1894
+ patch: makeSseFn("PATCH"),
1895
+ post: makeSseFn("POST"),
1896
+ put: makeSseFn("PUT"),
1897
+ trace: makeSseFn("TRACE")
1898
+ },
1899
+ trace: makeMethodFn("TRACE")
1900
+ };
1901
+ }, "createClient");
1902
+
1903
+ // src/_api/generated/client.gen.ts
1904
+ var client = createClient(createConfig({ baseUrl: "http://localhost:8000" }));
1905
+ installAuthOnClient(client);
1906
+
1907
+ // src/_api/generated/sdk.gen.ts
1908
+ var CfgAccountsOauth = class {
1909
+ static {
1910
+ __name(this, "CfgAccountsOauth");
1911
+ }
1912
+ /**
1913
+ * List OAuth connections
1914
+ *
1915
+ * Get all OAuth connections for the current user.
1916
+ */
1917
+ static cfgAccountsOauthConnectionsList(options) {
1918
+ return (options?.client ?? client).get({
1919
+ security: [
1920
+ { name: "X-API-Key", type: "apiKey" },
1921
+ { scheme: "bearer", type: "http" },
1922
+ { name: "Authorization", type: "apiKey" }
1923
+ ],
1924
+ url: "/cfg/accounts/oauth/connections/",
1925
+ ...options
1926
+ });
1927
+ }
1928
+ /**
1929
+ * Disconnect OAuth provider
1930
+ *
1931
+ * Remove OAuth connection for the specified provider.
1932
+ */
1933
+ static cfgAccountsOauthDisconnectCreate(options) {
1934
+ return (options.client ?? client).post({
1935
+ security: [
1936
+ { name: "X-API-Key", type: "apiKey" },
1937
+ { scheme: "bearer", type: "http" },
1938
+ { name: "Authorization", type: "apiKey" }
1939
+ ],
1940
+ url: "/cfg/accounts/oauth/disconnect/",
1941
+ ...options,
1942
+ headers: {
1943
+ "Content-Type": "application/json",
1944
+ ...options.headers
1945
+ }
1946
+ });
1947
+ }
1948
+ /**
1949
+ * Start GitHub OAuth
1950
+ *
1951
+ * Generate GitHub OAuth authorization URL. Redirect user to this URL to start authentication.
1952
+ */
1953
+ static cfgAccountsOauthGithubAuthorizeCreate(options) {
1954
+ return (options?.client ?? client).post({
1955
+ url: "/cfg/accounts/oauth/github/authorize/",
1956
+ ...options,
1957
+ headers: {
1958
+ "Content-Type": "application/json",
1959
+ ...options?.headers
1960
+ }
1961
+ });
1962
+ }
1963
+ /**
1964
+ * Complete GitHub OAuth
1965
+ *
1966
+ * Exchange authorization code for JWT tokens. Call this after GitHub redirects back with code.
1967
+ */
1968
+ static cfgAccountsOauthGithubCallbackCreate(options) {
1969
+ return (options.client ?? client).post({
1970
+ url: "/cfg/accounts/oauth/github/callback/",
1971
+ ...options,
1972
+ headers: {
1973
+ "Content-Type": "application/json",
1974
+ ...options.headers
1975
+ }
1976
+ });
1977
+ }
1978
+ /**
1979
+ * List OAuth providers
1980
+ *
1981
+ * Get list of available OAuth providers for authentication.
1982
+ */
1983
+ static cfgAccountsOauthProvidersRetrieve(options) {
1984
+ return (options?.client ?? client).get({ url: "/cfg/accounts/oauth/providers/", ...options });
1985
+ }
1986
+ };
1987
+ var CfgAccounts = class {
1988
+ static {
1989
+ __name(this, "CfgAccounts");
1990
+ }
1991
+ /**
1992
+ * Request OTP code to email.
1993
+ */
1994
+ static cfgAccountsOtpRequestCreate(options) {
1995
+ return (options.client ?? client).post({
1996
+ security: [
1997
+ { name: "X-API-Key", type: "apiKey" },
1998
+ { scheme: "bearer", type: "http" },
1999
+ { name: "Authorization", type: "apiKey" }
2000
+ ],
2001
+ url: "/cfg/accounts/otp/request/",
2002
+ ...options,
2003
+ headers: {
2004
+ "Content-Type": "application/json",
2005
+ ...options.headers
2006
+ }
2007
+ });
2008
+ }
2009
+ /**
2010
+ * Verify OTP code and return JWT tokens or 2FA session.
2011
+ *
2012
+ * If user has 2FA enabled:
2013
+ * - Returns requires_2fa=True with session_id
2014
+ * - Client must complete 2FA verification at /cfg/totp/verify/
2015
+ *
2016
+ * If user has no 2FA:
2017
+ * - Returns JWT tokens and user data directly
2018
+ */
2019
+ static cfgAccountsOtpVerifyCreate(options) {
2020
+ return (options.client ?? client).post({
2021
+ security: [
2022
+ { name: "X-API-Key", type: "apiKey" },
2023
+ { scheme: "bearer", type: "http" },
2024
+ { name: "Authorization", type: "apiKey" }
2025
+ ],
2026
+ url: "/cfg/accounts/otp/verify/",
2027
+ ...options,
2028
+ headers: {
2029
+ "Content-Type": "application/json",
2030
+ ...options.headers
2031
+ }
2032
+ });
2033
+ }
2034
+ };
2035
+ var CfgAccountsProfile = class {
2036
+ static {
2037
+ __name(this, "CfgAccountsProfile");
2038
+ }
2039
+ /**
2040
+ * Get current user profile
2041
+ *
2042
+ * Retrieve the current authenticated user's profile information.
2043
+ */
2044
+ static cfgAccountsProfileRetrieve(options) {
2045
+ return (options?.client ?? client).get({
2046
+ security: [{ scheme: "bearer", type: "http" }, {
2047
+ in: "cookie",
2048
+ name: "sessionid",
2049
+ type: "apiKey"
2050
+ }],
2051
+ url: "/cfg/accounts/profile/",
2052
+ ...options
2053
+ });
2054
+ }
2055
+ /**
2056
+ * Upload user avatar
2057
+ *
2058
+ * Upload avatar image for the current authenticated user. Accepts multipart/form-data with 'avatar' field.
2059
+ */
2060
+ static cfgAccountsProfileAvatarCreate(options) {
2061
+ return (options?.client ?? client).post({
2062
+ ...formDataBodySerializer,
2063
+ security: [
2064
+ { name: "X-API-Key", type: "apiKey" },
2065
+ { scheme: "bearer", type: "http" },
2066
+ { name: "Authorization", type: "apiKey" }
2067
+ ],
2068
+ url: "/cfg/accounts/profile/avatar/",
2069
+ ...options,
2070
+ headers: {
2071
+ "Content-Type": null,
2072
+ ...options?.headers
2073
+ }
2074
+ });
2075
+ }
2076
+ /**
2077
+ * Delete user account
2078
+ *
2079
+ *
2080
+ * Permanently delete the current user's account.
2081
+ *
2082
+ * This operation:
2083
+ * - Deactivates the account (user cannot log in)
2084
+ * - Anonymizes personal data (GDPR compliance)
2085
+ * - Frees up the email address for re-registration
2086
+ * - Preserves audit trail
2087
+ *
2088
+ * The account can be restored by an administrator if needed.
2089
+ *
2090
+ */
2091
+ static cfgAccountsProfileDeleteCreate(options) {
2092
+ return (options?.client ?? client).post({
2093
+ security: [{ scheme: "bearer", type: "http" }, {
2094
+ in: "cookie",
2095
+ name: "sessionid",
2096
+ type: "apiKey"
2097
+ }],
2098
+ url: "/cfg/accounts/profile/delete/",
2099
+ ...options
2100
+ });
2101
+ }
2102
+ /**
2103
+ * Partial update user profile
2104
+ *
2105
+ * Partially update the current authenticated user's profile information. Supports avatar upload.
2106
+ */
2107
+ static cfgAccountsProfilePartialPartialUpdate(options) {
2108
+ return (options?.client ?? client).patch({
2109
+ security: [{ scheme: "bearer", type: "http" }, {
2110
+ in: "cookie",
2111
+ name: "sessionid",
2112
+ type: "apiKey"
2113
+ }],
2114
+ url: "/cfg/accounts/profile/partial/",
2115
+ ...options,
2116
+ headers: {
2117
+ "Content-Type": "application/json",
2118
+ ...options?.headers
2119
+ }
2120
+ });
2121
+ }
2122
+ /**
2123
+ * Partial update user profile
2124
+ *
2125
+ * Partially update the current authenticated user's profile information. Supports avatar upload.
2126
+ */
2127
+ static cfgAccountsProfilePartialUpdate(options) {
2128
+ return (options?.client ?? client).put({
2129
+ security: [{ scheme: "bearer", type: "http" }, {
2130
+ in: "cookie",
2131
+ name: "sessionid",
2132
+ type: "apiKey"
2133
+ }],
2134
+ url: "/cfg/accounts/profile/partial/",
2135
+ ...options,
2136
+ headers: {
2137
+ "Content-Type": "application/json",
2138
+ ...options?.headers
2139
+ }
2140
+ });
2141
+ }
2142
+ /**
2143
+ * Update user profile
2144
+ *
2145
+ * Update the current authenticated user's profile information.
2146
+ */
2147
+ static cfgAccountsProfileUpdatePartialUpdate(options) {
2148
+ return (options?.client ?? client).patch({
2149
+ security: [{ scheme: "bearer", type: "http" }, {
2150
+ in: "cookie",
2151
+ name: "sessionid",
2152
+ type: "apiKey"
2153
+ }],
2154
+ url: "/cfg/accounts/profile/update/",
2155
+ ...options,
2156
+ headers: {
2157
+ "Content-Type": "application/json",
2158
+ ...options?.headers
2159
+ }
2160
+ });
2161
+ }
2162
+ /**
2163
+ * Update user profile
2164
+ *
2165
+ * Update the current authenticated user's profile information.
2166
+ */
2167
+ static cfgAccountsProfileUpdateUpdate(options) {
2168
+ return (options?.client ?? client).put({
2169
+ security: [{ scheme: "bearer", type: "http" }, {
2170
+ in: "cookie",
2171
+ name: "sessionid",
2172
+ type: "apiKey"
2173
+ }],
2174
+ url: "/cfg/accounts/profile/update/",
2175
+ ...options,
2176
+ headers: {
2177
+ "Content-Type": "application/json",
2178
+ ...options?.headers
2179
+ }
2180
+ });
2181
+ }
2182
+ };
2183
+ var CfgAccountsAuth = class {
2184
+ static {
2185
+ __name(this, "CfgAccountsAuth");
2186
+ }
2187
+ /**
2188
+ * Revoke a refresh token (logout).
2189
+ *
2190
+ * Blacklists the posted refresh token so it can never mint another access
2191
+ * token. Called best-effort by the client's logout — without it, "logout"
2192
+ * is purely client-side and a stolen refresh token survives until expiry.
2193
+ */
2194
+ static cfgAccountsTokenBlacklistCreate(options) {
2195
+ return (options.client ?? client).post({
2196
+ url: "/cfg/accounts/token/blacklist/",
2197
+ ...options,
2198
+ headers: {
2199
+ "Content-Type": "application/json",
2200
+ ...options.headers
2201
+ }
2202
+ });
2203
+ }
2204
+ /**
2205
+ * Refresh JWT token.
2206
+ *
2207
+ * DPoP-aware: when the incoming refresh token is key-bound (`cnf.jkt`), the
2208
+ * rotated access/refresh in the response are re-stamped with the same `cnf`
2209
+ * (stock SimpleJWT drops it from the derived access), and a matching DPoP
2210
+ * proof is required on the refresh request — so a stolen refresh token can't
2211
+ * be used to mint fresh tokens.
2212
+ */
2213
+ static cfgAccountsTokenRefreshCreate(options) {
2214
+ return (options.client ?? client).post({
2215
+ url: "/cfg/accounts/token/refresh/",
2216
+ ...options,
2217
+ headers: {
2218
+ "Content-Type": "application/json",
2219
+ ...options.headers
2220
+ }
2221
+ });
2222
+ }
2223
+ };
2224
+ var CfgTotp = class {
2225
+ static {
2226
+ __name(this, "CfgTotp");
2227
+ }
2228
+ /**
2229
+ * List all TOTP devices for user.
2230
+ */
2231
+ static cfgTotpDevicesRetrieve(options) {
2232
+ return (options?.client ?? client).get({
2233
+ security: [
2234
+ { name: "X-API-Key", type: "apiKey" },
2235
+ { scheme: "bearer", type: "http" },
2236
+ { name: "Authorization", type: "apiKey" }
2237
+ ],
2238
+ url: "/cfg/totp/devices/",
2239
+ ...options
2240
+ });
2241
+ }
2242
+ /**
2243
+ * Delete a TOTP device.
2244
+ *
2245
+ * Requires verification code if removing the last/primary device.
2246
+ */
2247
+ static cfgTotpDevicesDestroy(options) {
2248
+ return (options.client ?? client).delete({
2249
+ security: [
2250
+ { name: "X-API-Key", type: "apiKey" },
2251
+ { scheme: "bearer", type: "http" },
2252
+ { name: "Authorization", type: "apiKey" }
2253
+ ],
2254
+ url: "/cfg/totp/devices/{id}/",
2255
+ ...options
2256
+ });
2257
+ }
2258
+ /**
2259
+ * Completely disable 2FA for account.
2260
+ *
2261
+ * Requires verification code.
2262
+ */
2263
+ static cfgTotpDisableCreate(options) {
2264
+ return (options.client ?? client).post({
2265
+ security: [
2266
+ { name: "X-API-Key", type: "apiKey" },
2267
+ { scheme: "bearer", type: "http" },
2268
+ { name: "Authorization", type: "apiKey" }
2269
+ ],
2270
+ url: "/cfg/totp/disable/",
2271
+ ...options,
2272
+ headers: {
2273
+ "Content-Type": "application/json",
2274
+ ...options.headers
2275
+ }
2276
+ });
2277
+ }
2278
+ };
2279
+ var CfgTotpSetup = class {
2280
+ static {
2281
+ __name(this, "CfgTotpSetup");
2282
+ }
2283
+ /**
2284
+ * Start 2FA setup process.
2285
+ *
2286
+ * Creates a new TOTP device and returns QR code for scanning.
2287
+ */
2288
+ static cfgTotpSetupCreate(options) {
2289
+ return (options?.client ?? client).post({
2290
+ security: [
2291
+ { name: "X-API-Key", type: "apiKey" },
2292
+ { scheme: "bearer", type: "http" },
2293
+ { name: "Authorization", type: "apiKey" }
2294
+ ],
2295
+ url: "/cfg/totp/setup/",
2296
+ ...options,
2297
+ headers: {
2298
+ "Content-Type": "application/json",
2299
+ ...options?.headers
2300
+ }
2301
+ });
2302
+ }
2303
+ /**
2304
+ * Confirm 2FA setup with first valid code.
2305
+ *
2306
+ * Activates the device and generates backup codes.
2307
+ */
2308
+ static cfgTotpSetupConfirmCreate(options) {
2309
+ return (options.client ?? client).post({
2310
+ security: [
2311
+ { name: "X-API-Key", type: "apiKey" },
2312
+ { scheme: "bearer", type: "http" },
2313
+ { name: "Authorization", type: "apiKey" }
2314
+ ],
2315
+ url: "/cfg/totp/setup/confirm/",
2316
+ ...options,
2317
+ headers: {
2318
+ "Content-Type": "application/json",
2319
+ ...options.headers
2320
+ }
2321
+ });
2322
+ }
2323
+ };
2324
+ var CfgTotpVerify = class {
2325
+ static {
2326
+ __name(this, "CfgTotpVerify");
2327
+ }
2328
+ /**
2329
+ * Verify TOTP code for 2FA session.
2330
+ *
2331
+ * Completes authentication and returns JWT tokens on success.
2332
+ */
2333
+ static cfgTotpVerifyCreate(options) {
2334
+ return (options.client ?? client).post({
2335
+ security: [
2336
+ { name: "X-API-Key", type: "apiKey" },
2337
+ { scheme: "bearer", type: "http" },
2338
+ { name: "Authorization", type: "apiKey" }
2339
+ ],
2340
+ url: "/cfg/totp/verify/",
2341
+ ...options,
2342
+ headers: {
2343
+ "Content-Type": "application/json",
2344
+ ...options.headers
2345
+ }
2346
+ });
2347
+ }
2348
+ /**
2349
+ * Verify backup recovery code for 2FA session.
2350
+ *
2351
+ * Alternative verification method when TOTP device unavailable.
2352
+ */
2353
+ static cfgTotpVerifyBackupCreate(options) {
2354
+ return (options.client ?? client).post({
2355
+ security: [
2356
+ { name: "X-API-Key", type: "apiKey" },
2357
+ { scheme: "bearer", type: "http" },
2358
+ { name: "Authorization", type: "apiKey" }
2359
+ ],
2360
+ url: "/cfg/totp/verify/backup/",
2361
+ ...options,
2362
+ headers: {
2363
+ "Content-Type": "application/json",
2364
+ ...options.headers
2365
+ }
2366
+ });
2367
+ }
2368
+ };
2369
+
2370
+ // src/auth/utils/analytics.ts
2371
+ var AnalyticsEvent = /* @__PURE__ */ ((AnalyticsEvent2) => {
2372
+ AnalyticsEvent2["AUTH_OTP_REQUEST"] = "auth_otp_request";
2373
+ AnalyticsEvent2["AUTH_LOGIN_SUCCESS"] = "auth_login_success";
2374
+ AnalyticsEvent2["AUTH_OTP_VERIFY_FAIL"] = "auth_otp_verify_fail";
2375
+ AnalyticsEvent2["AUTH_SESSION_EXPIRED"] = "auth_session_expired";
2376
+ AnalyticsEvent2["AUTH_TOKEN_REFRESH"] = "auth_token_refresh";
2377
+ AnalyticsEvent2["AUTH_TOKEN_REFRESH_FAIL"] = "auth_token_refresh_fail";
2378
+ AnalyticsEvent2["AUTH_LOGOUT"] = "auth_logout";
2379
+ AnalyticsEvent2["AUTH_OAUTH_START"] = "auth_oauth_start";
2380
+ AnalyticsEvent2["AUTH_OAUTH_FAIL"] = "auth_oauth_fail";
2381
+ return AnalyticsEvent2;
2382
+ })(AnalyticsEvent || {});
2383
+ var AnalyticsCategory = /* @__PURE__ */ ((AnalyticsCategory2) => {
2384
+ AnalyticsCategory2["AUTH"] = "auth";
2385
+ return AnalyticsCategory2;
2386
+ })(AnalyticsCategory || {});
2387
+ var Analytics = {
2388
+ /**
2389
+ * Track an analytics event
2390
+ */
2391
+ event(eventName, params) {
2392
+ if (isDev) {
2393
+ console.log("[Analytics]", eventName, params);
2394
+ }
2395
+ },
2396
+ /**
2397
+ * Set user ID for tracking
2398
+ */
2399
+ setUser(userId) {
482
2400
  if (isDev) {
483
2401
  console.log("[Analytics] Set user:", userId);
484
2402
  }
485
2403
  }
486
- };
2404
+ };
2405
+
2406
+ // src/auth/hooks/useSessionStorage.ts
2407
+ import { useState as useState3 } from "react";
2408
+ function useSessionStorage(key, initialValue) {
2409
+ const [storedValue, setStoredValue] = useState3(() => {
2410
+ if (typeof window === "undefined") {
2411
+ return initialValue;
2412
+ }
2413
+ try {
2414
+ const item = window.sessionStorage.getItem(key);
2415
+ return item ? JSON.parse(item) : initialValue;
2416
+ } catch (error) {
2417
+ authLogger.error(`Error reading sessionStorage key "${key}":`, error);
2418
+ return initialValue;
2419
+ }
2420
+ });
2421
+ const checkDataSize = /* @__PURE__ */ __name((data) => {
2422
+ try {
2423
+ const jsonString = JSON.stringify(data);
2424
+ const sizeInBytes = new Blob([jsonString]).size;
2425
+ const sizeInKB = sizeInBytes / 1024;
2426
+ if (sizeInKB > 1024) {
2427
+ authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
2428
+ return false;
2429
+ }
2430
+ return true;
2431
+ } catch (error) {
2432
+ authLogger.error(`Error checking data size for key "${key}":`, error);
2433
+ return false;
2434
+ }
2435
+ }, "checkDataSize");
2436
+ const clearOldData = /* @__PURE__ */ __name(() => {
2437
+ try {
2438
+ const keys = Object.keys(sessionStorage).filter((key2) => key2 && typeof key2 === "string");
2439
+ if (keys.length > 50) {
2440
+ const itemsToRemove = Math.ceil(keys.length * 0.2);
2441
+ for (let i = 0; i < itemsToRemove; i++) {
2442
+ try {
2443
+ const key2 = keys[i];
2444
+ if (key2) {
2445
+ sessionStorage.removeItem(key2);
2446
+ sessionStorage.removeItem(`${key2}_timestamp`);
2447
+ }
2448
+ } catch {
2449
+ }
2450
+ }
2451
+ }
2452
+ } catch (error) {
2453
+ authLogger.error("Error clearing old sessionStorage data:", error);
2454
+ }
2455
+ }, "clearOldData");
2456
+ const forceClearAll = /* @__PURE__ */ __name(() => {
2457
+ try {
2458
+ const keys = Object.keys(sessionStorage);
2459
+ for (const key2 of keys) {
2460
+ try {
2461
+ sessionStorage.removeItem(key2);
2462
+ } catch {
2463
+ }
2464
+ }
2465
+ } catch (error) {
2466
+ authLogger.error("Error force clearing sessionStorage:", error);
2467
+ }
2468
+ }, "forceClearAll");
2469
+ const setValue = /* @__PURE__ */ __name((value) => {
2470
+ try {
2471
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
2472
+ if (!checkDataSize(valueToStore)) {
2473
+ authLogger.warn(`Data size too large for key "${key}", removing key`);
2474
+ try {
2475
+ window.sessionStorage.removeItem(key);
2476
+ window.sessionStorage.removeItem(`${key}_timestamp`);
2477
+ } catch {
2478
+ }
2479
+ setStoredValue(valueToStore);
2480
+ return;
2481
+ }
2482
+ setStoredValue(valueToStore);
2483
+ if (typeof window !== "undefined") {
2484
+ try {
2485
+ window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
2486
+ window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
2487
+ } catch (storageError) {
2488
+ if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
2489
+ authLogger.warn("sessionStorage quota exceeded, clearing old data...");
2490
+ clearOldData();
2491
+ try {
2492
+ window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
2493
+ window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
2494
+ } catch (retryError) {
2495
+ authLogger.error(`Failed to set sessionStorage key "${key}" after clearing old data:`, retryError);
2496
+ try {
2497
+ forceClearAll();
2498
+ window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
2499
+ window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
2500
+ } catch (finalError) {
2501
+ authLogger.error(`Failed to set sessionStorage key "${key}" after force clearing:`, finalError);
2502
+ setStoredValue(valueToStore);
2503
+ }
2504
+ }
2505
+ } else {
2506
+ throw storageError;
2507
+ }
2508
+ }
2509
+ }
2510
+ } catch (error) {
2511
+ authLogger.error(`Error setting sessionStorage key "${key}":`, error);
2512
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
2513
+ setStoredValue(valueToStore);
2514
+ }
2515
+ }, "setValue");
2516
+ const removeValue = /* @__PURE__ */ __name(() => {
2517
+ try {
2518
+ setStoredValue(initialValue);
2519
+ if (typeof window !== "undefined") {
2520
+ try {
2521
+ window.sessionStorage.removeItem(key);
2522
+ window.sessionStorage.removeItem(`${key}_timestamp`);
2523
+ } catch (removeError) {
2524
+ if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
2525
+ authLogger.warn("sessionStorage quota exceeded during removal, clearing old data...");
2526
+ clearOldData();
2527
+ try {
2528
+ window.sessionStorage.removeItem(key);
2529
+ window.sessionStorage.removeItem(`${key}_timestamp`);
2530
+ } catch (retryError) {
2531
+ authLogger.error(`Failed to remove sessionStorage key "${key}" after clearing:`, retryError);
2532
+ forceClearAll();
2533
+ }
2534
+ } else {
2535
+ throw removeError;
2536
+ }
2537
+ }
2538
+ }
2539
+ } catch (error) {
2540
+ authLogger.error(`Error removing sessionStorage key "${key}":`, error);
2541
+ }
2542
+ }, "removeValue");
2543
+ return [storedValue, setValue, removeValue];
2544
+ }
2545
+ __name(useSessionStorage, "useSessionStorage");
2546
+
2547
+ // src/auth/hooks/useAuthRedirect.ts
2548
+ var AUTH_REDIRECT_KEY = "auth_redirect_url";
2549
+ var getRedirectFromStorage = /* @__PURE__ */ __name(() => {
2550
+ if (typeof window === "undefined") return "";
2551
+ try {
2552
+ const item = window.sessionStorage.getItem(AUTH_REDIRECT_KEY);
2553
+ return item ? JSON.parse(item) : "";
2554
+ } catch {
2555
+ return "";
2556
+ }
2557
+ }, "getRedirectFromStorage");
2558
+ var clearRedirectFromStorage = /* @__PURE__ */ __name(() => {
2559
+ if (typeof window === "undefined") return;
2560
+ try {
2561
+ window.sessionStorage.removeItem(AUTH_REDIRECT_KEY);
2562
+ window.sessionStorage.removeItem(`${AUTH_REDIRECT_KEY}_timestamp`);
2563
+ } catch {
2564
+ }
2565
+ }, "clearRedirectFromStorage");
2566
+ var peekSavedRedirect = /* @__PURE__ */ __name(() => getRedirectFromStorage() || null, "peekSavedRedirect");
2567
+ var consumeSavedRedirect = /* @__PURE__ */ __name(() => {
2568
+ const stored = getRedirectFromStorage();
2569
+ if (stored) clearRedirectFromStorage();
2570
+ return stored || null;
2571
+ }, "consumeSavedRedirect");
2572
+ var useAuthRedirectManager = /* @__PURE__ */ __name((options = {}) => {
2573
+ const { fallbackUrl = "/dashboard", clearOnUse = true } = options;
2574
+ const [redirectUrl, setRedirectUrl, removeRedirectUrl] = useSessionStorage(AUTH_REDIRECT_KEY, "");
2575
+ const setRedirect = /* @__PURE__ */ __name((url) => {
2576
+ setRedirectUrl(url);
2577
+ }, "setRedirect");
2578
+ const getRedirect = /* @__PURE__ */ __name(() => {
2579
+ return getRedirectFromStorage() || redirectUrl;
2580
+ }, "getRedirect");
2581
+ const clearRedirect = /* @__PURE__ */ __name(() => {
2582
+ removeRedirectUrl();
2583
+ clearRedirectFromStorage();
2584
+ }, "clearRedirect");
2585
+ const hasRedirect = /* @__PURE__ */ __name(() => {
2586
+ const stored = getRedirectFromStorage();
2587
+ return stored.length > 0 || redirectUrl.length > 0;
2588
+ }, "hasRedirect");
2589
+ const getFinalRedirectUrl = /* @__PURE__ */ __name(() => {
2590
+ const stored = getRedirectFromStorage();
2591
+ return stored || redirectUrl || fallbackUrl;
2592
+ }, "getFinalRedirectUrl");
2593
+ const useAndClearRedirect = /* @__PURE__ */ __name(() => {
2594
+ const stored = getRedirectFromStorage();
2595
+ const finalUrl = stored || redirectUrl || fallbackUrl;
2596
+ if (clearOnUse) {
2597
+ clearRedirect();
2598
+ }
2599
+ return finalUrl;
2600
+ }, "useAndClearRedirect");
2601
+ return {
2602
+ redirectUrl,
2603
+ setRedirect,
2604
+ getRedirect,
2605
+ clearRedirect,
2606
+ hasRedirect,
2607
+ getFinalRedirectUrl,
2608
+ useAndClearRedirect
2609
+ };
2610
+ }, "useAuthRedirectManager");
487
2611
 
488
2612
  // src/auth/hooks/useTwoFactor.ts
489
2613
  var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
490
2614
  const { onSuccess, onError, redirectUrl, skipRedirect = false } = options;
491
2615
  const router = useCfgRouter();
492
- const [isLoading, setIsLoading] = useState3(false);
493
- const [error, setError] = useState3(null);
494
- const [warning, setWarning] = useState3(null);
495
- const [remainingBackupCodes, setRemainingBackupCodes] = useState3(null);
496
- const [attemptsRemaining, setAttemptsRemaining] = useState3(null);
2616
+ const [isLoading, setIsLoading] = useState4(false);
2617
+ const [error, setError] = useState4(null);
2618
+ const [warning, setWarning] = useState4(null);
2619
+ const [remainingBackupCodes, setRemainingBackupCodes] = useState4(null);
2620
+ const [attemptsRemaining, setAttemptsRemaining] = useState4(null);
497
2621
  const clearError = useCallback4(() => {
498
2622
  setError(null);
499
2623
  }, []);
@@ -514,7 +2638,7 @@ var useTwoFactor = /* @__PURE__ */ __name((options = {}) => {
514
2638
  }
515
2639
  onSuccess?.(response.user);
516
2640
  if (!skipRedirect) {
517
- const finalRedirectUrl = redirectUrl || "/dashboard";
2641
+ const finalRedirectUrl = consumeSavedRedirect() || redirectUrl || "/";
518
2642
  authLogger.info("2FA successful, redirecting to:", finalRedirectUrl);
519
2643
  router.hardPush(finalRedirectUrl);
520
2644
  }
@@ -904,12 +3028,12 @@ var useAuthForm = /* @__PURE__ */ __name((options) => {
904
3028
  }, "useAuthForm");
905
3029
 
906
3030
  // src/auth/hooks/useGithubAuth.ts
907
- import { useCallback as useCallback6, useState as useState4 } from "react";
3031
+ import { useCallback as useCallback6, useState as useState5 } from "react";
908
3032
  var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
909
3033
  const { sourceUrl, onSuccess, onError, onRequires2FA, redirectUrl, skipRedirect = false } = options;
910
3034
  const router = useCfgRouter();
911
- const [isLoading, setIsLoading] = useState4(false);
912
- const [error, setError] = useState4(null);
3035
+ const [isLoading, setIsLoading] = useState5(false);
3036
+ const [error, setError] = useState5(null);
913
3037
  const startGithubAuth = useCallback6(async () => {
914
3038
  setIsLoading(true);
915
3039
  setError(null);
@@ -1014,15 +3138,15 @@ var useGithubAuth = /* @__PURE__ */ __name((options = {}) => {
1014
3138
  }, "useGithubAuth");
1015
3139
 
1016
3140
  // src/auth/hooks/useTwoFactorSetup.ts
1017
- import { useCallback as useCallback7, useState as useState5 } from "react";
3141
+ import { useCallback as useCallback7, useState as useState6 } from "react";
1018
3142
  var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
1019
3143
  const { onComplete, onError } = options;
1020
- const [isLoading, setIsLoading] = useState5(false);
1021
- const [error, setError] = useState5(null);
1022
- const [setupData, setSetupData] = useState5(null);
1023
- const [backupCodes, setBackupCodes] = useState5(null);
1024
- const [backupCodesWarning, setBackupCodesWarning] = useState5(null);
1025
- const [setupStep, setSetupStep] = useState5("idle");
3144
+ const [isLoading, setIsLoading] = useState6(false);
3145
+ const [error, setError] = useState6(null);
3146
+ const [setupData, setSetupData] = useState6(null);
3147
+ const [backupCodes, setBackupCodes] = useState6(null);
3148
+ const [backupCodesWarning, setBackupCodesWarning] = useState6(null);
3149
+ const [setupStep, setSetupStep] = useState6("idle");
1026
3150
  const clearError = useCallback7(() => {
1027
3151
  setError(null);
1028
3152
  }, []);
@@ -1121,7 +3245,7 @@ var useTwoFactorSetup = /* @__PURE__ */ __name((options = {}) => {
1121
3245
  }, "useTwoFactorSetup");
1122
3246
 
1123
3247
  // src/auth/hooks/useTwoFactorStatus.ts
1124
- import { useCallback as useCallback8, useState as useState6 } from "react";
3248
+ import { useCallback as useCallback8, useState as useState7 } from "react";
1125
3249
  function extractErrorMessage(err, fallback) {
1126
3250
  if (err instanceof APIError) {
1127
3251
  const body = err.response;
@@ -1134,10 +3258,10 @@ function extractErrorMessage(err, fallback) {
1134
3258
  }
1135
3259
  __name(extractErrorMessage, "extractErrorMessage");
1136
3260
  var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
1137
- const [isLoading, setIsLoading] = useState6(false);
1138
- const [error, setError] = useState6(null);
1139
- const [has2FAEnabled, setHas2FAEnabled] = useState6(null);
1140
- const [devices, setDevices] = useState6([]);
3261
+ const [isLoading, setIsLoading] = useState7(false);
3262
+ const [error, setError] = useState7(null);
3263
+ const [has2FAEnabled, setHas2FAEnabled] = useState7(null);
3264
+ const [devices, setDevices] = useState7([]);
1141
3265
  const clearError = useCallback8(() => {
1142
3266
  setError(null);
1143
3267
  }, []);
@@ -1200,206 +3324,6 @@ var useTwoFactorStatus = /* @__PURE__ */ __name(() => {
1200
3324
  };
1201
3325
  }, "useTwoFactorStatus");
1202
3326
 
1203
- // src/auth/hooks/useSessionStorage.ts
1204
- import { useState as useState7 } from "react";
1205
- function useSessionStorage(key, initialValue) {
1206
- const [storedValue, setStoredValue] = useState7(() => {
1207
- if (typeof window === "undefined") {
1208
- return initialValue;
1209
- }
1210
- try {
1211
- const item = window.sessionStorage.getItem(key);
1212
- return item ? JSON.parse(item) : initialValue;
1213
- } catch (error) {
1214
- authLogger.error(`Error reading sessionStorage key "${key}":`, error);
1215
- return initialValue;
1216
- }
1217
- });
1218
- const checkDataSize = /* @__PURE__ */ __name((data) => {
1219
- try {
1220
- const jsonString = JSON.stringify(data);
1221
- const sizeInBytes = new Blob([jsonString]).size;
1222
- const sizeInKB = sizeInBytes / 1024;
1223
- if (sizeInKB > 1024) {
1224
- authLogger.warn(`Data size (${sizeInKB.toFixed(2)}KB) exceeds 1MB limit for key "${key}"`);
1225
- return false;
1226
- }
1227
- return true;
1228
- } catch (error) {
1229
- authLogger.error(`Error checking data size for key "${key}":`, error);
1230
- return false;
1231
- }
1232
- }, "checkDataSize");
1233
- const clearOldData = /* @__PURE__ */ __name(() => {
1234
- try {
1235
- const keys = Object.keys(sessionStorage).filter((key2) => key2 && typeof key2 === "string");
1236
- if (keys.length > 50) {
1237
- const itemsToRemove = Math.ceil(keys.length * 0.2);
1238
- for (let i = 0; i < itemsToRemove; i++) {
1239
- try {
1240
- const key2 = keys[i];
1241
- if (key2) {
1242
- sessionStorage.removeItem(key2);
1243
- sessionStorage.removeItem(`${key2}_timestamp`);
1244
- }
1245
- } catch {
1246
- }
1247
- }
1248
- }
1249
- } catch (error) {
1250
- authLogger.error("Error clearing old sessionStorage data:", error);
1251
- }
1252
- }, "clearOldData");
1253
- const forceClearAll = /* @__PURE__ */ __name(() => {
1254
- try {
1255
- const keys = Object.keys(sessionStorage);
1256
- for (const key2 of keys) {
1257
- try {
1258
- sessionStorage.removeItem(key2);
1259
- } catch {
1260
- }
1261
- }
1262
- } catch (error) {
1263
- authLogger.error("Error force clearing sessionStorage:", error);
1264
- }
1265
- }, "forceClearAll");
1266
- const setValue = /* @__PURE__ */ __name((value) => {
1267
- try {
1268
- const valueToStore = value instanceof Function ? value(storedValue) : value;
1269
- if (!checkDataSize(valueToStore)) {
1270
- authLogger.warn(`Data size too large for key "${key}", removing key`);
1271
- try {
1272
- window.sessionStorage.removeItem(key);
1273
- window.sessionStorage.removeItem(`${key}_timestamp`);
1274
- } catch {
1275
- }
1276
- setStoredValue(valueToStore);
1277
- return;
1278
- }
1279
- setStoredValue(valueToStore);
1280
- if (typeof window !== "undefined") {
1281
- try {
1282
- window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
1283
- window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
1284
- } catch (storageError) {
1285
- if (storageError.name === "QuotaExceededError" || storageError.code === 22 || storageError.message?.includes("quota")) {
1286
- authLogger.warn("sessionStorage quota exceeded, clearing old data...");
1287
- clearOldData();
1288
- try {
1289
- window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
1290
- window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
1291
- } catch (retryError) {
1292
- authLogger.error(`Failed to set sessionStorage key "${key}" after clearing old data:`, retryError);
1293
- try {
1294
- forceClearAll();
1295
- window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
1296
- window.sessionStorage.setItem(`${key}_timestamp`, Date.now().toString());
1297
- } catch (finalError) {
1298
- authLogger.error(`Failed to set sessionStorage key "${key}" after force clearing:`, finalError);
1299
- setStoredValue(valueToStore);
1300
- }
1301
- }
1302
- } else {
1303
- throw storageError;
1304
- }
1305
- }
1306
- }
1307
- } catch (error) {
1308
- authLogger.error(`Error setting sessionStorage key "${key}":`, error);
1309
- const valueToStore = value instanceof Function ? value(storedValue) : value;
1310
- setStoredValue(valueToStore);
1311
- }
1312
- }, "setValue");
1313
- const removeValue = /* @__PURE__ */ __name(() => {
1314
- try {
1315
- setStoredValue(initialValue);
1316
- if (typeof window !== "undefined") {
1317
- try {
1318
- window.sessionStorage.removeItem(key);
1319
- window.sessionStorage.removeItem(`${key}_timestamp`);
1320
- } catch (removeError) {
1321
- if (removeError.name === "QuotaExceededError" || removeError.code === 22 || removeError.message?.includes("quota")) {
1322
- authLogger.warn("sessionStorage quota exceeded during removal, clearing old data...");
1323
- clearOldData();
1324
- try {
1325
- window.sessionStorage.removeItem(key);
1326
- window.sessionStorage.removeItem(`${key}_timestamp`);
1327
- } catch (retryError) {
1328
- authLogger.error(`Failed to remove sessionStorage key "${key}" after clearing:`, retryError);
1329
- forceClearAll();
1330
- }
1331
- } else {
1332
- throw removeError;
1333
- }
1334
- }
1335
- }
1336
- } catch (error) {
1337
- authLogger.error(`Error removing sessionStorage key "${key}":`, error);
1338
- }
1339
- }, "removeValue");
1340
- return [storedValue, setValue, removeValue];
1341
- }
1342
- __name(useSessionStorage, "useSessionStorage");
1343
-
1344
- // src/auth/hooks/useAuthRedirect.ts
1345
- var AUTH_REDIRECT_KEY = "auth_redirect_url";
1346
- var getRedirectFromStorage = /* @__PURE__ */ __name(() => {
1347
- if (typeof window === "undefined") return "";
1348
- try {
1349
- const item = window.sessionStorage.getItem(AUTH_REDIRECT_KEY);
1350
- return item ? JSON.parse(item) : "";
1351
- } catch {
1352
- return "";
1353
- }
1354
- }, "getRedirectFromStorage");
1355
- var clearRedirectFromStorage = /* @__PURE__ */ __name(() => {
1356
- if (typeof window === "undefined") return;
1357
- try {
1358
- window.sessionStorage.removeItem(AUTH_REDIRECT_KEY);
1359
- window.sessionStorage.removeItem(`${AUTH_REDIRECT_KEY}_timestamp`);
1360
- } catch {
1361
- }
1362
- }, "clearRedirectFromStorage");
1363
- var useAuthRedirectManager = /* @__PURE__ */ __name((options = {}) => {
1364
- const { fallbackUrl = "/dashboard", clearOnUse = true } = options;
1365
- const [redirectUrl, setRedirectUrl, removeRedirectUrl] = useSessionStorage(AUTH_REDIRECT_KEY, "");
1366
- const setRedirect = /* @__PURE__ */ __name((url) => {
1367
- setRedirectUrl(url);
1368
- }, "setRedirect");
1369
- const getRedirect = /* @__PURE__ */ __name(() => {
1370
- return getRedirectFromStorage() || redirectUrl;
1371
- }, "getRedirect");
1372
- const clearRedirect = /* @__PURE__ */ __name(() => {
1373
- removeRedirectUrl();
1374
- clearRedirectFromStorage();
1375
- }, "clearRedirect");
1376
- const hasRedirect = /* @__PURE__ */ __name(() => {
1377
- const stored = getRedirectFromStorage();
1378
- return stored.length > 0 || redirectUrl.length > 0;
1379
- }, "hasRedirect");
1380
- const getFinalRedirectUrl = /* @__PURE__ */ __name(() => {
1381
- const stored = getRedirectFromStorage();
1382
- return stored || redirectUrl || fallbackUrl;
1383
- }, "getFinalRedirectUrl");
1384
- const useAndClearRedirect = /* @__PURE__ */ __name(() => {
1385
- const stored = getRedirectFromStorage();
1386
- const finalUrl = stored || redirectUrl || fallbackUrl;
1387
- if (clearOnUse) {
1388
- clearRedirect();
1389
- }
1390
- return finalUrl;
1391
- }, "useAndClearRedirect");
1392
- return {
1393
- redirectUrl,
1394
- setRedirect,
1395
- getRedirect,
1396
- clearRedirect,
1397
- hasRedirect,
1398
- getFinalRedirectUrl,
1399
- useAndClearRedirect
1400
- };
1401
- }, "useAuthRedirectManager");
1402
-
1403
3327
  // src/auth/hooks/useLocalStorage.ts
1404
3328
  import { useState as useState8 } from "react";
1405
3329
  function useLocalStorage(key, initialValue) {
@@ -2445,10 +4369,15 @@ __name(useAccountsContext, "useAccountsContext");
2445
4369
  // src/auth/context/AuthContext.tsx
2446
4370
  import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
2447
4371
  var defaultRoutes = {
2448
- auth: DEFAULT_AUTH_PATH,
2449
- defaultCallback: DEFAULT_CALLBACK_PATH,
2450
- defaultAuthCallback: DEFAULT_AUTH_PATH
4372
+ auth: "/auth",
4373
+ defaultCallback: "/",
4374
+ defaultAuthCallback: "/auth"
2451
4375
  };
4376
+ var resolveAuthRoutes = /* @__PURE__ */ __name((routes) => ({
4377
+ auth: routes?.auth || defaultRoutes.auth,
4378
+ defaultCallback: routes?.defaultCallback || defaultRoutes.defaultCallback,
4379
+ defaultAuthCallback: routes?.defaultAuthCallback || routes?.auth || defaultRoutes.defaultAuthCallback
4380
+ }), "resolveAuthRoutes");
2452
4381
  var EMAIL_STORAGE_KEY = "auth_email";
2453
4382
  var AuthContext = createContext2(void 0);
2454
4383
  var apiErrorMessage = /* @__PURE__ */ __name((error) => {
@@ -2466,8 +4395,13 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2466
4395
  EMAIL_STORAGE_KEY,
2467
4396
  null
2468
4397
  );
4398
+ const routes = useMemo3(() => resolveAuthRoutes(config?.routes), [config?.routes]);
4399
+ const routesRef = useRef6(routes);
4400
+ useEffect7(() => {
4401
+ routesRef.current = routes;
4402
+ }, [routes]);
2469
4403
  const redirectManager = useAuthRedirectManager({
2470
- fallbackUrl: config?.routes?.defaultCallback || defaultRoutes.defaultCallback,
4404
+ fallbackUrl: routes.defaultCallback,
2471
4405
  clearOnUse: true
2472
4406
  });
2473
4407
  const configRef = useRef6(config);
@@ -2478,13 +4412,15 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2478
4412
  useEffect7(() => {
2479
4413
  accountsRef.current = accounts;
2480
4414
  }, [accounts]);
4415
+ const pendingManualNavRef = useRef6(false);
2481
4416
  const [profileSettled, setProfileSettled] = useState11(false);
2482
4417
  const hasProfile = Boolean(accounts.profile);
2483
4418
  const profileAttemptedRef = useRef6(false);
2484
4419
  useEffect7(() => {
2485
4420
  if (!isAuthenticated) {
2486
4421
  profileAttemptedRef.current = false;
2487
- if (auth.getToken() || auth.getRefreshToken()) {
4422
+ const hasTokens = Boolean(auth.getToken() || auth.getRefreshToken());
4423
+ if (hasTokens && !auth.isAuthenticated()) {
2488
4424
  authLogger.warn("Dead session in storage \u2014 clearing");
2489
4425
  auth.clearSession();
2490
4426
  clearProfileCache();
@@ -2518,18 +4454,18 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2518
4454
  Analytics.event("auth_session_expired" /* AUTH_SESSION_EXPIRED */, {
2519
4455
  category: "auth" /* AUTH */
2520
4456
  });
2521
- const authCallbackUrl = configRef.current?.routes?.defaultAuthCallback || defaultRoutes.defaultAuthCallback;
2522
- router.hardReplace(authCallbackUrl);
4457
+ router.hardReplace(routesRef.current.defaultAuthCallback);
2523
4458
  });
2524
4459
  return unsubscribe;
2525
4460
  }, [router]);
2526
4461
  useEffect7(() => {
2527
- if (!isAuthenticated) return;
2528
- const authRoute = config?.routes?.auth || defaultRoutes.auth;
2529
- if (pathname === authRoute && !queryParams.get("flow")) {
2530
- router.push(config?.routes?.defaultCallback || defaultRoutes.defaultCallback);
4462
+ if (!isAuthenticated || pendingManualNavRef.current) return;
4463
+ if (pathname === routes.auth && !queryParams.get("flow")) {
4464
+ const saved = redirectManager.hasRedirect() ? redirectManager.getRedirect() : null;
4465
+ if (saved) redirectManager.clearRedirect();
4466
+ router.push(saved || routes.defaultCallback);
2531
4467
  }
2532
- }, [isAuthenticated, pathname, queryParams, config?.routes, router]);
4468
+ }, [isAuthenticated, pathname, queryParams, routes, router, redirectManager]);
2533
4469
  const loadCurrentProfile = useCallback11(async (callerId) => {
2534
4470
  if (!auth.isAuthenticated()) {
2535
4471
  throw new Error("No valid authentication token");
@@ -2539,16 +4475,15 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2539
4475
  });
2540
4476
  }, []);
2541
4477
  const checkAuthAndRedirect = useCallback11(async () => {
2542
- const routes = configRef.current?.routes;
2543
4478
  if (auth.isAuthenticated()) {
2544
4479
  try {
2545
4480
  await loadCurrentProfile("checkAuthAndRedirect");
2546
4481
  } catch (error) {
2547
4482
  authLogger.warn("checkAuthAndRedirect: profile load failed", error);
2548
4483
  }
2549
- router.push(routes?.defaultCallback || defaultRoutes.defaultCallback);
4484
+ router.push(routesRef.current.defaultCallback);
2550
4485
  } else {
2551
- router.push(routes?.defaultAuthCallback || defaultRoutes.defaultAuthCallback);
4486
+ router.push(routesRef.current.defaultAuthCallback);
2552
4487
  }
2553
4488
  }, [loadCurrentProfile, router]);
2554
4489
  const requestOTP = useCallback11(
@@ -2588,6 +4523,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2588
4523
  const verifyOTP = useCallback11(
2589
4524
  async (identifier, otpCode, sourceUrl, redirectUrl, skipRedirect) => {
2590
4525
  try {
4526
+ pendingManualNavRef.current = Boolean(skipRedirect);
2591
4527
  const result = await accountsRef.current.verifyOTP({
2592
4528
  identifier,
2593
4529
  otp: otpCode,
@@ -2618,8 +4554,9 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2618
4554
  Analytics.setUser(String(result.user.id));
2619
4555
  }
2620
4556
  if (!skipRedirect) {
2621
- const savedRedirect = redirectManager.useAndClearRedirect();
2622
- const finalRedirectUrl = redirectUrl || savedRedirect || configRef.current?.routes?.defaultCallback || defaultRoutes.defaultCallback;
4557
+ const saved = redirectManager.hasRedirect() ? redirectManager.getRedirect() : null;
4558
+ if (saved) redirectManager.clearRedirect();
4559
+ const finalRedirectUrl = saved || redirectUrl || routesRef.current.defaultCallback;
2623
4560
  authLogger.info("Redirecting after auth to:", finalRedirectUrl);
2624
4561
  router.hardPush(finalRedirectUrl);
2625
4562
  }
@@ -2661,8 +4598,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2661
4598
  category: "auth" /* AUTH */
2662
4599
  });
2663
4600
  accountsRef.current.logout();
2664
- const authCallbackUrl = configRef.current?.routes?.defaultAuthCallback || defaultRoutes.defaultAuthCallback;
2665
- router.hardReplace(authCallbackUrl);
4601
+ router.hardReplace(routesRef.current.defaultAuthCallback);
2666
4602
  }, [router]);
2667
4603
  const user = accounts.profile ?? null;
2668
4604
  const isAdminUser = Boolean(user?.is_staff || user?.is_superuser);
@@ -2688,6 +4624,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2688
4624
  // expiry timer, cross-tab/cross-store sync) — no ticks, no polling.
2689
4625
  isAuthenticated,
2690
4626
  isAdminUser,
4627
+ routes,
2691
4628
  loadCurrentProfile,
2692
4629
  checkAuthAndRedirect,
2693
4630
  getToken: /* @__PURE__ */ __name(() => auth.getToken(), "getToken"),
@@ -2711,6 +4648,7 @@ var AuthProviderInternal = /* @__PURE__ */ __name(({ children, config }) => {
2711
4648
  isLoading,
2712
4649
  isAuthenticated,
2713
4650
  isAdminUser,
4651
+ routes,
2714
4652
  loadCurrentProfile,
2715
4653
  checkAuthAndRedirect,
2716
4654
  storedEmail,
@@ -2738,6 +4676,7 @@ var defaultAuthState = {
2738
4676
  isLoading: false,
2739
4677
  isAuthenticated: false,
2740
4678
  isAdminUser: false,
4679
+ routes: resolveAuthRoutes(),
2741
4680
  loadCurrentProfile: /* @__PURE__ */ __name(async () => {
2742
4681
  authLogger.warn("useAuth: loadCurrentProfile called outside AuthProvider");
2743
4682
  }, "loadCurrentProfile"),
@@ -2785,7 +4724,7 @@ var defaultAuthState = {
2785
4724
  var useAuth = /* @__PURE__ */ __name(() => {
2786
4725
  const context = useContext2(AuthContext);
2787
4726
  if (context === void 0) {
2788
- if (isBrowser && isDev) {
4727
+ if (isBrowser2 && isDev) {
2789
4728
  authLogger.debug("useAuth called outside AuthProvider, returning default state");
2790
4729
  }
2791
4730
  return defaultAuthState;
@@ -2840,11 +4779,10 @@ export {
2840
4779
  AnalyticsEvent,
2841
4780
  AuthContext_default as AuthContext,
2842
4781
  AuthProvider,
2843
- DEFAULT_AUTH_PATH,
2844
- DEFAULT_CALLBACK_PATH,
2845
4782
  PatchedCfgUserUpdateRequestSchema,
2846
4783
  authLogger,
2847
4784
  clearProfileCache,
4785
+ consumeSavedRedirect,
2848
4786
  decodeBase64,
2849
4787
  encodeBase64,
2850
4788
  formatAuthError,
@@ -2854,6 +4792,7 @@ export {
2854
4792
  isAllowedAuthPath,
2855
4793
  logger,
2856
4794
  normalizePath,
4795
+ peekSavedRedirect,
2857
4796
  resolveGuardIsAuthenticated,
2858
4797
  resolveGuardIsLoading,
2859
4798
  setCachedProfile,