@djangocfg/api 2.1.602 → 2.1.604

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.d.cts CHANGED
@@ -61,6 +61,11 @@ declare const auth: {
61
61
  * the mode is module state and resets on reload, so a later switch strands
62
62
  * whatever was written after it. */
63
63
  setStorageMode(mode: StorageMode): void;
64
+ /** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
65
+ * cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
66
+ setCookieDomain(domain: "auto" | string | null): void;
67
+ /** The `Domain` the session cookie is written with; `null` = host-only. */
68
+ getCookieDomain(): string | null;
64
69
  getToken(): string | null;
65
70
  setToken(token: string | null): void;
66
71
  getRefreshToken(): string | null;
@@ -2601,6 +2606,16 @@ interface StorageOptions {
2601
2606
  readonly families?: Readonly<Record<string, FamilySpec>>;
2602
2607
  /** Cookie lifetime for the `cookie` store. Default 30 days. */
2603
2608
  readonly cookieMaxAgeSeconds?: number;
2609
+ /**
2610
+ * Cookie `Domain`. `'auto'` (default): the registrable domain of the page, so every subdomain
2611
+ * shares the cookie (`example.com` for `my.example.com`). A string forces a domain; `false`
2612
+ * keeps cookies host-only.
2613
+ */
2614
+ readonly cookieDomain?: 'auto' | string | false;
2615
+ }
2616
+ interface WriteOptions {
2617
+ /** Cookie store: lifetime of this write. Default: the owner's `cookieMaxAgeSeconds`. */
2618
+ readonly maxAgeSeconds?: number;
2604
2619
  }
2605
2620
  interface GuardedStorage {
2606
2621
  readonly owner: string;
@@ -2612,7 +2627,7 @@ interface GuardedStorage {
2612
2627
  isValid(key: string): boolean;
2613
2628
  read(key: string, store?: KeyStore): string | null;
2614
2629
  /** True when the value reached the browser's storage; false means it is held in memory for this page only. */
2615
- write(key: string, value: string, store?: KeyStore): boolean;
2630
+ write(key: string, value: string, store?: KeyStore, options?: WriteOptions): boolean;
2616
2631
  /** Removes the key. */
2617
2632
  remove(key: string, store?: KeyStore): void;
2618
2633
  readJSON<T>(key: string, store?: KeyStore): T | null;
@@ -2623,6 +2638,10 @@ interface GuardedStorage {
2623
2638
  clearAll(options?: {
2624
2639
  all?: boolean;
2625
2640
  }): void;
2641
+ /** Cookie store: change the `Domain` policy (`'auto'`, a domain, or `false` for host-only cookies). */
2642
+ setCookieDomain(domain: 'auto' | string | false): void;
2643
+ /** The `Domain` cookies are written with right now; `null` means host-only. */
2644
+ cookieDomain(): string | null;
2626
2645
  /** Cross-tab: calls `listener` with the new value when ANOTHER tab changes `key`. Cookies fire no events. */
2627
2646
  onChange(key: string, listener: (value: string | null) => void, store?: Exclude<KeyStore, 'cookie'>): () => void;
2628
2647
  }
package/dist/index.d.ts CHANGED
@@ -61,6 +61,11 @@ declare const auth: {
61
61
  * the mode is module state and resets on reload, so a later switch strands
62
62
  * whatever was written after it. */
63
63
  setStorageMode(mode: StorageMode): void;
64
+ /** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
65
+ * cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
66
+ setCookieDomain(domain: "auto" | string | null): void;
67
+ /** The `Domain` the session cookie is written with; `null` = host-only. */
68
+ getCookieDomain(): string | null;
64
69
  getToken(): string | null;
65
70
  setToken(token: string | null): void;
66
71
  getRefreshToken(): string | null;
@@ -2601,6 +2606,16 @@ interface StorageOptions {
2601
2606
  readonly families?: Readonly<Record<string, FamilySpec>>;
2602
2607
  /** Cookie lifetime for the `cookie` store. Default 30 days. */
2603
2608
  readonly cookieMaxAgeSeconds?: number;
2609
+ /**
2610
+ * Cookie `Domain`. `'auto'` (default): the registrable domain of the page, so every subdomain
2611
+ * shares the cookie (`example.com` for `my.example.com`). A string forces a domain; `false`
2612
+ * keeps cookies host-only.
2613
+ */
2614
+ readonly cookieDomain?: 'auto' | string | false;
2615
+ }
2616
+ interface WriteOptions {
2617
+ /** Cookie store: lifetime of this write. Default: the owner's `cookieMaxAgeSeconds`. */
2618
+ readonly maxAgeSeconds?: number;
2604
2619
  }
2605
2620
  interface GuardedStorage {
2606
2621
  readonly owner: string;
@@ -2612,7 +2627,7 @@ interface GuardedStorage {
2612
2627
  isValid(key: string): boolean;
2613
2628
  read(key: string, store?: KeyStore): string | null;
2614
2629
  /** True when the value reached the browser's storage; false means it is held in memory for this page only. */
2615
- write(key: string, value: string, store?: KeyStore): boolean;
2630
+ write(key: string, value: string, store?: KeyStore, options?: WriteOptions): boolean;
2616
2631
  /** Removes the key. */
2617
2632
  remove(key: string, store?: KeyStore): void;
2618
2633
  readJSON<T>(key: string, store?: KeyStore): T | null;
@@ -2623,6 +2638,10 @@ interface GuardedStorage {
2623
2638
  clearAll(options?: {
2624
2639
  all?: boolean;
2625
2640
  }): void;
2641
+ /** Cookie store: change the `Domain` policy (`'auto'`, a domain, or `false` for host-only cookies). */
2642
+ setCookieDomain(domain: 'auto' | string | false): void;
2643
+ /** The `Domain` cookies are written with right now; `null` means host-only. */
2644
+ cookieDomain(): string | null;
2626
2645
  /** Cross-tab: calls `listener` with the new value when ANOTHER tab changes `key`. Cookies fire no events. */
2627
2646
  onChange(key: string, listener: (value: string | null) => void, store?: Exclude<KeyStore, 'cookie'>): () => void;
2628
2647
  }
package/dist/index.mjs CHANGED
@@ -82,8 +82,8 @@ var NetworkError = class extends Error {
82
82
  var k = /* @__PURE__ */ __name((key, store, personal) => ({ key, store, personal }), "k");
83
83
  var f = /* @__PURE__ */ __name((prefix, store, personal) => ({ prefix, store, personal }), "f");
84
84
  var CFG_KEYS = {
85
- accessToken: k("cfg.auth.access_token", "local", true),
86
- refreshToken: k("cfg.auth.refresh_token", "local", true),
85
+ accessToken: k("cfg.auth.access_token", "cookie", true),
86
+ refreshToken: k("cfg.auth.refresh_token", "cookie", true),
87
87
  apiKey: k("cfg.auth.api_key", "local", true),
88
88
  email: k("cfg.auth.email", "local", false),
89
89
  emailTs: k("cfg.auth.email_ts", "local", false),
@@ -118,6 +118,31 @@ function normalizeSegment(raw) {
118
118
  }
119
119
  __name(normalizeSegment, "normalizeSegment");
120
120
  var hasWindow = /* @__PURE__ */ __name(() => typeof window !== "undefined", "hasWindow");
121
+ var domainByHost = /* @__PURE__ */ new Map();
122
+ function detectCookieDomain() {
123
+ if (!hasWindow() || typeof document === "undefined") return null;
124
+ const host = window.location.hostname.toLowerCase();
125
+ if (!host || !host.includes(".") || host.includes(":") || /^[0-9.]+$/.test(host)) return null;
126
+ const cached = domainByHost.get(host);
127
+ if (cached !== void 0) return cached;
128
+ const labels = host.split(".");
129
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
130
+ let found = null;
131
+ try {
132
+ for (let i = labels.length - 2; i >= 0 && found === null; i -= 1) {
133
+ const candidate = labels.slice(i).join(".");
134
+ const probe = "cfg_probe_" + Math.random().toString(36).slice(2);
135
+ document.cookie = probe + "=1; Domain=" + candidate + "; Path=/; Max-Age=30; SameSite=Lax" + secure;
136
+ if (document.cookie.includes(probe + "=1")) found = candidate;
137
+ document.cookie = probe + "=; Domain=" + candidate + "; Path=/; Max-Age=0; SameSite=Lax" + secure;
138
+ }
139
+ } catch {
140
+ found = null;
141
+ }
142
+ domainByHost.set(host, found);
143
+ return found;
144
+ }
145
+ __name(detectCookieDomain, "detectCookieDomain");
121
146
  function nativeStorage(store) {
122
147
  if (!hasWindow()) return null;
123
148
  try {
@@ -143,6 +168,12 @@ function createStorage(owner, options = {}) {
143
168
  const prefix = `${owner}.`;
144
169
  const pattern = owner === "cfg" ? /^cfg(\.[a-z0-9_]+)+$/ : new RegExp(`^(cfg|${owner})(\\.[a-z0-9_]+)+$`);
145
170
  const cookieMaxAge = options.cookieMaxAgeSeconds ?? 60 * 60 * 24 * 30;
171
+ let cookiePolicy = options.cookieDomain ?? "auto";
172
+ const resolveCookieDomain = /* @__PURE__ */ __name(() => cookiePolicy === false ? null : cookiePolicy === "auto" ? detectCookieDomain() : cookiePolicy, "resolveCookieDomain");
173
+ const expireCookie = /* @__PURE__ */ __name((key, domain) => {
174
+ const secure = window.location.protocol === "https:" ? "; Secure" : "";
175
+ document.cookie = encodeURIComponent(key) + "=; Path=/; Max-Age=0; SameSite=Lax" + (domain ? "; Domain=" + domain : "") + secure;
176
+ }, "expireCookie");
146
177
  const specs = Object.values(options.keys ?? {});
147
178
  const families = Object.values(options.families ?? {});
148
179
  const specByKey = /* @__PURE__ */ new Map();
@@ -179,12 +210,15 @@ function createStorage(owner, options = {}) {
179
210
  return memory[store].get(key) ?? null;
180
211
  }
181
212
  __name(rawGet, "rawGet");
182
- function rawSet(store, key, value) {
213
+ function rawSet(store, key, value, maxAgeSeconds) {
183
214
  if (!hasWindow()) return false;
184
215
  try {
185
216
  if (store === "cookie") {
186
217
  const secure = window.location.protocol === "https:" ? "; Secure" : "";
187
- document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax${secure}`;
218
+ const domain = resolveCookieDomain();
219
+ const domainPart = domain ? `; Domain=${domain}` : "";
220
+ document.cookie = `${encodeURIComponent(key)}=${encodeURIComponent(value)}; Path=/; Max-Age=${Math.max(1, Math.floor(maxAgeSeconds ?? cookieMaxAge))}; SameSite=Lax${domainPart}${secure}`;
221
+ if (domain) expireCookie(key, null);
188
222
  if (rawGet("cookie", key) === value && !memory.cookie.has(key)) return true;
189
223
  memory.cookie.set(key, value);
190
224
  return false;
@@ -205,8 +239,9 @@ function createStorage(owner, options = {}) {
205
239
  if (!hasWindow()) return;
206
240
  try {
207
241
  if (store === "cookie") {
208
- const secure = window.location.protocol === "https:" ? "; Secure" : "";
209
- document.cookie = `${encodeURIComponent(key)}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
242
+ expireCookie(key, null);
243
+ const domain = resolveCookieDomain();
244
+ if (domain) expireCookie(key, domain);
210
245
  } else {
211
246
  nativeStorage(store)?.removeItem(key);
212
247
  }
@@ -255,10 +290,10 @@ function createStorage(owner, options = {}) {
255
290
  const target = resolveStore(key, store);
256
291
  return rawGet(target, key);
257
292
  },
258
- write(key, value, store) {
293
+ write(key, value, store, writeOptions) {
259
294
  assertOwned(key);
260
295
  const target = resolveStore(key, store);
261
- return rawSet(target, key, value);
296
+ return rawSet(target, key, value, writeOptions?.maxAgeSeconds);
262
297
  },
263
298
  remove(key, store) {
264
299
  assertOwned(key);
@@ -296,6 +331,10 @@ function createStorage(owner, options = {}) {
296
331
  for (const name of cookieNames()) if (name.startsWith(prefix)) rawRemove("cookie", name);
297
332
  }
298
333
  },
334
+ setCookieDomain(domain) {
335
+ cookiePolicy = domain;
336
+ },
337
+ cookieDomain: resolveCookieDomain,
299
338
  onChange(key, listener, store = "local") {
300
339
  assertValid(key);
301
340
  if (!hasWindow()) return () => {
@@ -322,6 +361,11 @@ var REFRESH_KEY = "cfg.auth.refresh_token";
322
361
  var API_KEY_KEY = "cfg.auth.api_key";
323
362
  var storage = true ? cfgStorage : createStorage2("cfg");
324
363
  var isBrowser = typeof window !== "undefined";
364
+ function secondsUntilExp(token) {
365
+ const exp = jwtExpMs(token);
366
+ return exp === null ? void 0 : Math.max(1, Math.floor((exp - Date.now()) / 1e3));
367
+ }
368
+ __name(secondsUntilExp, "secondsUntilExp");
325
369
  var STORE_OF = {
326
370
  localStorage: "local",
327
371
  sessionStorage: "session",
@@ -333,13 +377,31 @@ function backendFor(mode) {
333
377
  get: /* @__PURE__ */ __name((key) => storage.read(key, store), "get"),
334
378
  set(key, value) {
335
379
  if (value === null) storage.remove(key, store);
336
- else storage.write(key, value, store);
380
+ else storage.write(key, value, store, store === "cookie" && key === REFRESH_KEY ? { maxAgeSeconds: secondsUntilExp(value) } : void 0);
337
381
  }
338
382
  };
339
383
  }
340
384
  __name(backendFor, "backendFor");
341
- var _storageMode = "localStorage";
385
+ var _storageMode = "cookie";
342
386
  var _storage = backendFor(_storageMode);
387
+ var _apiKeyStore = backendFor("localStorage");
388
+ function adoptLegacySession() {
389
+ if (!isBrowser || _storageMode !== "cookie") return;
390
+ try {
391
+ if (_storage.get(ACCESS_KEY) !== null || _storage.get(REFRESH_KEY) !== null) return;
392
+ const legacy = backendFor("localStorage");
393
+ const access = legacy.get(ACCESS_KEY);
394
+ const refresh = legacy.get(REFRESH_KEY);
395
+ if (access === null && refresh === null) return;
396
+ if (access !== null) _storage.set(ACCESS_KEY, access);
397
+ if (refresh !== null) _storage.set(REFRESH_KEY, refresh);
398
+ legacy.set(ACCESS_KEY, null);
399
+ legacy.set(REFRESH_KEY, null);
400
+ } catch {
401
+ }
402
+ }
403
+ __name(adoptLegacySession, "adoptLegacySession");
404
+ adoptLegacySession();
343
405
  function detectLocale() {
344
406
  try {
345
407
  if (typeof document === "undefined") return null;
@@ -484,6 +546,14 @@ function ensureStorageSync() {
484
546
  }
485
547
  });
486
548
  window.addEventListener(SESSION_SYNC_EVENT, () => notifySessionChanged());
549
+ window.addEventListener("focus", () => notifySessionChanged());
550
+ document.addEventListener("visibilitychange", () => {
551
+ if (document.visibilityState === "visible") notifySessionChanged();
552
+ });
553
+ try {
554
+ window.cookieStore?.addEventListener("change", () => notifySessionChanged());
555
+ } catch {
556
+ }
487
557
  }
488
558
  __name(ensureStorageSync, "ensureStorageSync");
489
559
  var SESSION_EXPIRED_REGISTRY = `cfg-auth:expired-handlers:${ACCESS_KEY}`;
@@ -517,6 +587,17 @@ var auth = {
517
587
  _storage = backendFor(mode);
518
588
  notifySessionChanged();
519
589
  },
590
+ // ── Shared cookie domain ──────────────────────────────────────────
591
+ /** `'auto'` (default): the registrable domain of the page. A domain forces it; `null` keeps
592
+ * cookies host-only (no sign-in sharing between subdomains). Call before sign-in. */
593
+ setCookieDomain(domain) {
594
+ storage.setCookieDomain(domain === null ? false : domain);
595
+ notifySessionChanged();
596
+ },
597
+ /** The `Domain` the session cookie is written with; `null` = host-only. */
598
+ getCookieDomain() {
599
+ return storage.cookieDomain();
600
+ },
520
601
  // ── Bearer token ──────────────────────────────────────────────────
521
602
  getToken() {
522
603
  return _storage.get(ACCESS_KEY);
@@ -603,18 +684,18 @@ var auth = {
603
684
  },
604
685
  // ── API key ───────────────────────────────────────────────────────
605
686
  getApiKey() {
606
- return _apiKeyOverride ?? _storage.get(API_KEY_KEY) ?? defaultApiKey();
687
+ return _apiKeyOverride ?? _apiKeyStore.get(API_KEY_KEY) ?? defaultApiKey();
607
688
  },
608
689
  setApiKey(key) {
609
690
  _apiKeyOverride = key;
610
691
  },
611
692
  setApiKeyPersist(key) {
612
693
  _apiKeyOverride = key;
613
- _storage.set(API_KEY_KEY, key);
694
+ _apiKeyStore.set(API_KEY_KEY, key);
614
695
  },
615
696
  clearApiKey() {
616
697
  _apiKeyOverride = null;
617
- _storage.set(API_KEY_KEY, null);
698
+ _apiKeyStore.set(API_KEY_KEY, null);
618
699
  },
619
700
  // ── Locale ────────────────────────────────────────────────────────
620
701
  getLocale() {