@sparkvault/sdk 1.21.6 → 1.21.7

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.
@@ -7,6 +7,15 @@
7
7
  export { CooldownTimer, ExpirationTimer } from './utils/cooldown-timer';
8
8
  export type { CooldownTimerConfig, ExpirationTimerConfig } from './utils/cooldown-timer';
9
9
  export { arrayBufferToBase64url, base64urlToArrayBuffer, base64urlToString, } from '../utils/base64url';
10
+ /**
11
+ * Extract the root (registrable) domain from a hostname for cross-subdomain cookies.
12
+ * Returns the domain prefixed with a dot (e.g., ".client.com") or null when
13
+ * the domain attribute should not be set (localhost, IP addresses).
14
+ *
15
+ * @param hostname - The hostname to extract the root domain from
16
+ * @returns Root domain with leading dot, or null
17
+ */
18
+ export declare function getRootDomain(hostname: string): string | null;
10
19
  /**
11
20
  * Get cookie value by name
12
21
  * @param name - Cookie name
@@ -14,7 +23,10 @@ export { arrayBufferToBase64url, base64urlToArrayBuffer, base64urlToString, } fr
14
23
  */
15
24
  export declare function getCookie(name: string): string | null;
16
25
  /**
17
- * Set cookie with expiration
26
+ * Set cookie with expiration on the root domain for cross-subdomain access.
27
+ * The domain is automatically extracted from the current hostname and prefixed
28
+ * with a dot (e.g., ".client.com") so the cookie is shared across all subdomains.
29
+ *
18
30
  * @param name - Cookie name
19
31
  * @param value - Cookie value
20
32
  * @param days - Days until expiration
@@ -269,6 +269,7 @@ class HttpClient {
269
269
  }
270
270
  async parseResponse(response) {
271
271
  // Handle empty responses (204 No Content, etc.)
272
+ // Callers expecting no body should use <void> as the generic type parameter.
272
273
  const contentLength = response.headers.get('content-length');
273
274
  if (response.status === 204 || contentLength === '0') {
274
275
  return null;
@@ -543,6 +544,57 @@ function base64urlToString(base64url) {
543
544
  * Consolidates duplicated logic from renderer, api, and index.
544
545
  */
545
546
  // Re-export timer utilities
547
+ /**
548
+ * Common multi-part public suffixes where the last two labels form the TLD.
549
+ * Domains under these suffixes need three labels for a valid registrable domain
550
+ * (e.g., "app.client.co.uk" → registrable domain is "client.co.uk").
551
+ */
552
+ const MULTI_PART_TLDS = new Set([
553
+ 'co.uk', 'co.jp', 'co.kr', 'co.nz', 'co.za', 'co.in', 'co.id', 'co.th',
554
+ 'com.au', 'com.br', 'com.cn', 'com.mx', 'com.sg', 'com.tw', 'com.hk',
555
+ 'com.ar', 'com.co', 'com.my', 'com.ph', 'com.pk', 'com.tr', 'com.ua',
556
+ 'com.vn', 'com.ng', 'com.eg', 'com.sa', 'com.pe', 'com.ec',
557
+ 'net.au', 'net.br', 'net.cn', 'net.nz',
558
+ 'org.au', 'org.br', 'org.cn', 'org.nz', 'org.uk',
559
+ 'ac.uk', 'gov.uk', 'ac.jp', 'ne.jp', 'or.jp',
560
+ 'ac.kr', 'go.kr', 'or.kr',
561
+ 'ac.nz', 'govt.nz',
562
+ 'ac.za', 'gov.za',
563
+ 'ac.in', 'gov.in', 'net.in', 'org.in',
564
+ ]);
565
+ /**
566
+ * Extract the root (registrable) domain from a hostname for cross-subdomain cookies.
567
+ * Returns the domain prefixed with a dot (e.g., ".client.com") or null when
568
+ * the domain attribute should not be set (localhost, IP addresses).
569
+ *
570
+ * @param hostname - The hostname to extract the root domain from
571
+ * @returns Root domain with leading dot, or null
572
+ */
573
+ function getRootDomain(hostname) {
574
+ // Localhost — browsers reject domain attribute for localhost
575
+ if (hostname === 'localhost' || hostname === '127.0.0.1')
576
+ return null;
577
+ // IPv4 addresses
578
+ if (/^\d{1,3}(\.\d{1,3}){3}$/.test(hostname))
579
+ return null;
580
+ // IPv6 addresses
581
+ if (hostname.includes(':'))
582
+ return null;
583
+ const parts = hostname.split('.');
584
+ // Single-label hostnames (no dots)
585
+ if (parts.length <= 1)
586
+ return null;
587
+ // Two-part domain (e.g., "client.com") — already the root
588
+ if (parts.length === 2)
589
+ return `.${hostname}`;
590
+ // Check for multi-part TLD (e.g., "co.uk" in "app.client.co.uk")
591
+ const lastTwo = parts.slice(-2).join('.');
592
+ if (MULTI_PART_TLDS.has(lastTwo) && parts.length >= 3) {
593
+ return `.${parts.slice(-3).join('.')}`;
594
+ }
595
+ // Standard TLD — root domain is last two parts (e.g., "app.client.com" → ".client.com")
596
+ return `.${lastTwo}`;
597
+ }
546
598
  /**
547
599
  * Get cookie value by name
548
600
  * @param name - Cookie name
@@ -553,7 +605,10 @@ function getCookie(name) {
553
605
  return match ? match[2] : null;
554
606
  }
555
607
  /**
556
- * Set cookie with expiration
608
+ * Set cookie with expiration on the root domain for cross-subdomain access.
609
+ * The domain is automatically extracted from the current hostname and prefixed
610
+ * with a dot (e.g., ".client.com") so the cookie is shared across all subdomains.
611
+ *
557
612
  * @param name - Cookie name
558
613
  * @param value - Cookie value
559
614
  * @param days - Days until expiration
@@ -562,7 +617,9 @@ function setCookie(name, value, days) {
562
617
  const date = new Date();
563
618
  date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
564
619
  const expires = `expires=${date.toUTCString()}`;
565
- document.cookie = `${name}=${value}; ${expires}; path=/; SameSite=Lax`;
620
+ const domain = getRootDomain(window.location.hostname);
621
+ const domainAttr = domain ? `; domain=${domain}` : '';
622
+ document.cookie = `${name}=${value}; ${expires}; path=/${domainAttr}; SameSite=Lax`;
566
623
  }
567
624
  /**
568
625
  * Generate cryptographically secure random state string
@@ -614,9 +671,11 @@ class IdentityApi {
614
671
  }
615
672
  const url = `${this.baseUrl}${endpoint}`;
616
673
  const headers = {
617
- 'Content-Type': 'application/json',
618
674
  'Accept': 'application/json',
619
675
  };
676
+ if (body) {
677
+ headers['Content-Type'] = 'application/json';
678
+ }
620
679
  // Create abort controller for request timeout
621
680
  const timeoutController = new AbortController();
622
681
  const timeoutId = setTimeout(() => timeoutController.abort(), this.timeoutMs);