@djangocfg/api 2.1.618 → 2.1.621

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -64,6 +64,141 @@ __export(index_exports, {
64
64
  });
65
65
  module.exports = __toCommonJS(index_exports);
66
66
 
67
+ // src/auth/trace.ts
68
+ var KEY = "cfg.auth.trace";
69
+ var MAX = 200;
70
+ var MAX_STRING = 120;
71
+ var SENSITIVE = /token|otp|code|email|password|secret|cookie|authorization|key|identifier/i;
72
+ var TAB = Math.random().toString(36).slice(2, 6);
73
+ var hasWindow = typeof window !== "undefined";
74
+ var memory = [];
75
+ var loaded = false;
76
+ function backend() {
77
+ if (!hasWindow) return null;
78
+ for (const name of ["localStorage", "sessionStorage"]) {
79
+ try {
80
+ const s = window[name];
81
+ s.setItem("__cfg_probe", "1");
82
+ s.removeItem("__cfg_probe");
83
+ return s;
84
+ } catch {
85
+ }
86
+ }
87
+ return null;
88
+ }
89
+ __name(backend, "backend");
90
+ function load() {
91
+ if (loaded) return memory;
92
+ loaded = true;
93
+ try {
94
+ const raw = backend()?.getItem(KEY);
95
+ const parsed = raw ? JSON.parse(raw) : [];
96
+ if (Array.isArray(parsed)) memory = parsed.filter((e) => typeof e === "object" && e !== null);
97
+ } catch {
98
+ memory = [];
99
+ }
100
+ return memory;
101
+ }
102
+ __name(load, "load");
103
+ function persist() {
104
+ try {
105
+ backend()?.setItem(KEY, JSON.stringify(memory));
106
+ } catch {
107
+ }
108
+ }
109
+ __name(persist, "persist");
110
+ function clean(fields) {
111
+ if (!fields) return void 0;
112
+ const out = {};
113
+ for (const [k2, v] of Object.entries(fields)) {
114
+ if (SENSITIVE.test(k2)) continue;
115
+ if (typeof v === "string") out[k2] = v.length > MAX_STRING ? `${v.slice(0, MAX_STRING)}\u2026` : v;
116
+ else if (typeof v === "number" || typeof v === "boolean" || v === null) out[k2] = v;
117
+ }
118
+ return Object.keys(out).length ? out : void 0;
119
+ }
120
+ __name(clean, "clean");
121
+ function record(at, ev, fields) {
122
+ const entries = load();
123
+ const p = hasWindow && typeof performance !== "undefined" ? Math.round(performance.now()) : 0;
124
+ entries.push({ t: at, p, tab: TAB, ev, f: clean(fields) });
125
+ if (entries.length > MAX) entries.splice(0, entries.length - MAX);
126
+ persist();
127
+ }
128
+ __name(record, "record");
129
+ function authTrace(ev, fields) {
130
+ if (!hasWindow) return;
131
+ try {
132
+ record(Date.now(), ev, fields);
133
+ } catch {
134
+ }
135
+ }
136
+ __name(authTrace, "authTrace");
137
+ function readAuthTrace() {
138
+ return load().slice();
139
+ }
140
+ __name(readAuthTrace, "readAuthTrace");
141
+ function clearAuthTrace() {
142
+ memory = [];
143
+ loaded = true;
144
+ try {
145
+ backend()?.removeItem(KEY);
146
+ } catch {
147
+ }
148
+ }
149
+ __name(clearAuthTrace, "clearAuthTrace");
150
+ function context() {
151
+ if (!hasWindow) return [];
152
+ const lines = [`url: ${window.location.href.split("?")[0]}`, `online: ${navigator.onLine}`, `ua: ${navigator.userAgent}`];
153
+ try {
154
+ const auth2 = globalThis.__cfgAuthNow;
155
+ const now = auth2?.();
156
+ if (now) lines.push(`now: ${Object.entries(now).map(([k2, v]) => `${k2}=${String(v)}`).join(" ")}`);
157
+ } catch {
158
+ }
159
+ return lines;
160
+ }
161
+ __name(context, "context");
162
+ function dumpAuthTrace() {
163
+ const entries = load();
164
+ const first = entries[0]?.t ?? Date.now();
165
+ const rows = entries.map((e) => {
166
+ const fields = e.f ? ` ${Object.entries(e.f).map(([k2, v]) => `${k2}=${String(v)}`).join(" ")}` : "";
167
+ const at = new Date(e.t).toISOString().slice(11, 23);
168
+ return `${at} +${String(e.t - first).padStart(6)}ms [${e.tab} p${e.p}] ${e.ev}${fields}`;
169
+ });
170
+ return [`# cfg auth trace, ${entries.length} events`, ...context(), "", ...rows].join("\n");
171
+ }
172
+ __name(dumpAuthTrace, "dumpAuthTrace");
173
+ function installAuthTrace() {
174
+ if (!hasWindow) return;
175
+ const g = globalThis;
176
+ g.__cfgAuthTrace = authTrace;
177
+ g.__cfgAuth = {
178
+ trace: readAuthTrace,
179
+ dump: /* @__PURE__ */ __name(() => {
180
+ const text = dumpAuthTrace();
181
+ console.log(text);
182
+ return text;
183
+ }, "dump"),
184
+ clear: clearAuthTrace
185
+ };
186
+ const queued = g.__cfgAuthTraceQ;
187
+ if (Array.isArray(queued)) {
188
+ g.__cfgAuthTraceQ = [];
189
+ for (const item of queued) {
190
+ if (Array.isArray(item) && typeof item[1] === "string") {
191
+ try {
192
+ record(Number(item[0]) || Date.now(), item[1], item[2]);
193
+ } catch {
194
+ }
195
+ }
196
+ }
197
+ }
198
+ }
199
+ __name(installAuthTrace, "installAuthTrace");
200
+ installAuthTrace();
201
+
67
202
  // src/_api/generated/helpers/errors.ts
68
203
  var APIError = class extends Error {
69
204
  constructor(statusCode, statusText, response, url, message) {
@@ -180,10 +315,10 @@ function normalizeSegment(raw) {
180
315
  return raw.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "");
181
316
  }
182
317
  __name(normalizeSegment, "normalizeSegment");
183
- var hasWindow = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
318
+ var hasWindow2 = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
184
319
  var domainByHost = /* @__PURE__ */ new Map();
185
320
  function detectCookieDomain() {
186
- if (!hasWindow() || typeof document === "undefined") return null;
321
+ if (!hasWindow2() || typeof document === "undefined") return null;
187
322
  const host = window.location.hostname.toLowerCase();
188
323
  if (!host || !host.includes(".") || host.includes(":") || /^[0-9.]+$/.test(host)) return null;
189
324
  const cached = domainByHost.get(host);
@@ -207,7 +342,7 @@ function detectCookieDomain() {
207
342
  }
208
343
  __name(detectCookieDomain, "detectCookieDomain");
209
344
  function nativeStorage(store) {
210
- if (!hasWindow()) return null;
345
+ if (!hasWindow2()) return null;
211
346
  try {
212
347
  return store === "session" ? window.sessionStorage : window.localStorage;
213
348
  } catch {
@@ -216,7 +351,7 @@ function nativeStorage(store) {
216
351
  }
217
352
  __name(nativeStorage, "nativeStorage");
218
353
  function cookieNames() {
219
- if (!hasWindow()) return [];
354
+ if (!hasWindow2()) return [];
220
355
  try {
221
356
  return document.cookie.split("; ").map((part) => decodeURIComponent(part.split("=")[0] ?? "")).filter(Boolean);
222
357
  } catch {
@@ -241,7 +376,7 @@ function createStorage(owner, options = {}) {
241
376
  const families = Object.values(options.families ?? {});
242
377
  const specByKey = /* @__PURE__ */ new Map();
243
378
  for (const spec of specs) specByKey.set(spec.key, [...specByKey.get(spec.key) ?? [], spec]);
244
- const memory = {
379
+ const memory2 = {
245
380
  local: /* @__PURE__ */ new Map(),
246
381
  session: /* @__PURE__ */ new Map(),
247
382
  cookie: /* @__PURE__ */ new Map()
@@ -255,7 +390,7 @@ function createStorage(owner, options = {}) {
255
390
  if (!key.startsWith(prefix)) throw new TypeError(`storage key "${key}" is not owned by "${owner}"`);
256
391
  }, "assertOwned");
257
392
  function rawGet(store, key) {
258
- if (!hasWindow()) return null;
393
+ if (!hasWindow2()) return null;
259
394
  if (store === "cookie") {
260
395
  try {
261
396
  const escaped = encodeURIComponent(key).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -263,18 +398,18 @@ function createStorage(owner, options = {}) {
263
398
  if (match?.[1] !== void 0) return decodeURIComponent(match[1]);
264
399
  } catch {
265
400
  }
266
- return memory.cookie.get(key) ?? null;
401
+ return memory2.cookie.get(key) ?? null;
267
402
  }
268
403
  try {
269
404
  const value = nativeStorage(store)?.getItem(key) ?? null;
270
405
  if (value !== null) return value;
271
406
  } catch {
272
407
  }
273
- return memory[store].get(key) ?? null;
408
+ return memory2[store].get(key) ?? null;
274
409
  }
275
410
  __name(rawGet, "rawGet");
276
411
  function rawSet(store, key, value, maxAgeSeconds) {
277
- if (!hasWindow()) return false;
412
+ if (!hasWindow2()) return false;
278
413
  try {
279
414
  if (store === "cookie") {
280
415
  const secure = window.location.protocol === "https:" ? "; Secure" : "";
@@ -282,24 +417,24 @@ function createStorage(owner, options = {}) {
282
417
  const domainPart = domain ? `; Domain=${domain}` : "";
283
418
  document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${Math.max(1, Math.floor(maxAgeSeconds ?? cookieMaxAge))}; SameSite=Lax${domainPart}${secure}`;
284
419
  if (domain) expireCookie(key, null);
285
- if (rawGet("cookie", key) === value && !memory.cookie.has(key)) return true;
286
- memory.cookie.set(key, value);
420
+ if (rawGet("cookie", key) === value && !memory2.cookie.has(key)) return true;
421
+ memory2.cookie.set(key, value);
287
422
  return false;
288
423
  }
289
424
  const native = nativeStorage(store);
290
425
  if (!native) throw new Error("storage unavailable");
291
426
  native.setItem(key, value);
292
- memory[store].delete(key);
427
+ memory2[store].delete(key);
293
428
  return true;
294
429
  } catch {
295
- memory[store].set(key, value);
430
+ memory2[store].set(key, value);
296
431
  return false;
297
432
  }
298
433
  }
299
434
  __name(rawSet, "rawSet");
300
435
  function rawRemove(store, key) {
301
- memory[store].delete(key);
302
- if (!hasWindow()) return;
436
+ memory2[store].delete(key);
437
+ if (!hasWindow2()) return;
303
438
  try {
304
439
  if (store === "cookie") {
305
440
  expireCookie(key, null);
@@ -328,7 +463,7 @@ function createStorage(owner, options = {}) {
328
463
  }
329
464
  } catch {
330
465
  }
331
- for (const name of memory[store].keys()) if (name.startsWith(startsWith) && !out.includes(name)) out.push(name);
466
+ for (const name of memory2[store].keys()) if (name.startsWith(startsWith) && !out.includes(name)) out.push(name);
332
467
  return out;
333
468
  }
334
469
  __name(listKeys, "listKeys");
@@ -400,7 +535,7 @@ function createStorage(owner, options = {}) {
400
535
  cookieDomain: resolveCookieDomain,
401
536
  onChange(key, listener, store = "local") {
402
537
  assertValid(key);
403
- if (!hasWindow()) return () => {
538
+ if (!hasWindow2()) return () => {
404
539
  };
405
540
  const handler = /* @__PURE__ */ __name((event) => {
406
541
  if (event.storageArea !== nativeStorage(store)) return;
@@ -424,6 +559,29 @@ var REFRESH_KEY = "cfg.auth.refresh_token";
424
559
  var API_KEY_KEY = "cfg.auth.api_key";
425
560
  var storage = true ? cfgStorage : createStorage2("cfg");
426
561
  var isBrowser = typeof window !== "undefined";
562
+ function trace(ev, fields) {
563
+ try {
564
+ const g = globalThis;
565
+ const sink = g.__cfgAuthTrace;
566
+ if (sink) {
567
+ sink(ev, fields);
568
+ return;
569
+ }
570
+ const queue = g.__cfgAuthTraceQ ?? [];
571
+ g.__cfgAuthTraceQ = queue;
572
+ if (queue.length < 100) queue.push([Date.now(), ev, fields]);
573
+ } catch {
574
+ }
575
+ }
576
+ __name(trace, "trace");
577
+ function pathOf(url) {
578
+ try {
579
+ return new URL(url, "http://x").pathname;
580
+ } catch {
581
+ return "";
582
+ }
583
+ }
584
+ __name(pathOf, "pathOf");
427
585
  function secondsUntilExp(token) {
428
586
  const exp = jwtExpMs(token);
429
587
  return exp === null ? void 0 : Math.max(1, Math.floor((exp - Date.now()) / 1e3));
@@ -583,7 +741,10 @@ __name(scheduleExpiryFlip, "scheduleExpiryFlip");
583
741
  function notifySessionChanged() {
584
742
  const next = computeSnapshot();
585
743
  const changed = next.status !== _snapshot.status || next.accessExpiresAt !== _snapshot.accessExpiresAt;
586
- if (changed) _snapshot = next;
744
+ if (changed) {
745
+ _snapshot = next;
746
+ trace("store.snapshot", { status: next.status });
747
+ }
587
748
  scheduleExpiryFlip();
588
749
  if (!changed) return;
589
750
  for (const listener of Array.from(_sessionListeners)) {
@@ -666,6 +827,7 @@ var auth = {
666
827
  return _storage.get(ACCESS_KEY);
667
828
  },
668
829
  setToken(token) {
830
+ trace("store.setToken", { present: token !== null });
669
831
  _storage.set(ACCESS_KEY, token);
670
832
  notifySessionChanged();
671
833
  },
@@ -673,14 +835,16 @@ var auth = {
673
835
  return _storage.get(REFRESH_KEY);
674
836
  },
675
837
  setRefreshToken(token) {
838
+ trace("store.setRefreshToken", { present: token !== null });
676
839
  _storage.set(REFRESH_KEY, token);
677
840
  notifySessionChanged();
678
841
  },
679
842
  clearTokens() {
843
+ trace("store.clearTokens");
680
844
  for (const mode of ["localStorage", "sessionStorage", "cookie"]) {
681
- const backend = backendFor(mode);
682
- backend.set(ACCESS_KEY, null);
683
- backend.set(REFRESH_KEY, null);
845
+ const backend2 = backendFor(mode);
846
+ backend2.set(ACCESS_KEY, null);
847
+ backend2.set(REFRESH_KEY, null);
684
848
  }
685
849
  storage.clearAll();
686
850
  notifySessionChanged();
@@ -699,6 +863,7 @@ var auth = {
699
863
  * rotation); `refresh: null` explicitly drops it.
700
864
  */
701
865
  setSession(tokens) {
866
+ trace("store.setSession", { refresh: tokens.refresh === void 0 ? "kept" : tokens.refresh === null ? "dropped" : "set" });
702
867
  _storage.set(ACCESS_KEY, tokens.access);
703
868
  if (tokens.refresh !== void 0) _storage.set(REFRESH_KEY, tokens.refresh);
704
869
  notifySessionChanged();
@@ -837,7 +1002,11 @@ async function tryRefresh() {
837
1002
  const refresh = auth.getRefreshToken();
838
1003
  if (!refresh) return null;
839
1004
  const result = await _refreshHandler(refresh);
840
- if (!result?.access) return null;
1005
+ if (!result?.access) {
1006
+ trace("refresh", { ok: false, why: "no-access" });
1007
+ return null;
1008
+ }
1009
+ trace("refresh", { ok: true, rotated: Boolean(result.refresh) });
841
1010
  auth.setToken(result.access);
842
1011
  if (result.refresh) auth.setRefreshToken(result.refresh);
843
1012
  return result.access;
@@ -850,6 +1019,7 @@ async function tryRefresh() {
850
1019
  }
851
1020
  return await runRefresh();
852
1021
  } catch {
1022
+ trace("refresh", { ok: false, why: "threw" });
853
1023
  return null;
854
1024
  } finally {
855
1025
  _refreshInflight = null;
@@ -858,7 +1028,8 @@ async function tryRefresh() {
858
1028
  return _refreshInflight;
859
1029
  }
860
1030
  __name(tryRefresh, "tryRefresh");
861
- function expireSession(response) {
1031
+ function expireSession(response, why) {
1032
+ trace("store.expire", { why, status: response.status, path: pathOf(response.url), handlers: _sessionExpiredHandlers.size });
862
1033
  auth.clearTokens();
863
1034
  for (const cb of Array.from(_sessionExpiredHandlers)) {
864
1035
  try {
@@ -1000,13 +1171,17 @@ function installAuthOnClient(client2) {
1000
1171
  });
1001
1172
  client2.interceptors.response.use(async (response, request) => {
1002
1173
  if (response.status !== 401) return response;
1174
+ if (/\/accounts\/(otp|oauth|login|register|token)\b/.test(request.url)) {
1175
+ trace("401.passthrough", { path: pathOf(request.url) });
1176
+ return response;
1177
+ }
1003
1178
  if (request.headers.get(RETRY_MARKER)) {
1004
- expireSession(response);
1179
+ expireSession(response, "retry-marker");
1005
1180
  return response;
1006
1181
  }
1007
1182
  const newToken = await tryRefresh();
1008
1183
  if (!newToken) {
1009
- expireSession(response);
1184
+ expireSession(response, "refresh-failed");
1010
1185
  return response;
1011
1186
  }
1012
1187
  const retry = request.clone();
@@ -1018,7 +1193,7 @@ function installAuthOnClient(client2) {
1018
1193
  }
1019
1194
  try {
1020
1195
  const retried = await fetch(retry);
1021
- if (retried.status === 401) expireSession(retried);
1196
+ if (retried.status === 401) expireSession(retried, "retry-401");
1022
1197
  return retried;
1023
1198
  } catch {
1024
1199
  return response;